2010-11-17 13 views
5

Wygląda na to, że formant DataGridView może wiązać tylko ze źródłami danych, które są płaskie (wszystkie właściwości są typami pierwotnymi). Moje dane są hierarchiczne. Na przykład:Poszukując obejścia dla niezdolności kontroli DataGridView do powiązania z danymi hierarchicznymi (OO)

interface INestedObj 
{ 
    string Prop3 { get; } 
} 

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

Biorąc to pod uwagę, w jaki sposób wiąże się do obiektu realizującego IParentObj? W końcu masz do zrobienia coś takiego:

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 

Szukam porady i/lub obejść.

TIA

Odpowiedz

3

Oto proste rozwiązanie, które pojawiło się pod koniec długiego dnia.

Użyłem zapytania i projekcji Linq do stworzenia anonimowego typu, który wyświetla odpowiednie informacje w DataGridView.

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

musiałem dostarczyć odpowiednią wartość (NestedObj.Prop3) do nieruchomości DataPropertyName uzyskać wartość do wyświetlenia w siatce.

Kiedy mam więcej czasu, spróbuję wdrożyć rozwiązanie Bradleya.

+1

To jest znacznie bardziej eleganckie rozwiązanie, ale musisz poświęcić takie rzeczy jak powiadomienie o zmianie (jeśli twoje obiekty go wspierają, nie wiem). Ostatnią rzeczą, na którą warto spojrzeć, jest interfejs "ITypedList", który kolekcja może zaimplementować w celu odsłonięcia właściwości niestandardowych. Jest trochę mniej flakey niż rozwiązanie "TypeDescriptorProvider", które napisałem, po refleksji. –

2

Można chyba dodać kolumnę niezwiązanego dla „NestedObj.Prop3” i ręcznie obsługiwać swoją wartość. Aby wypełnić kolumnę, należy obsłużyć zdarzenie CellFormatting w DataGridView, pobrać obiekt DataBoundItem z bieżącego wiersza i pobrać z niego prop3. Aby zaktualizować źródło danych, należy obsłużyć zdarzenie CellValidated w celu zaktualizowania obiektu DataBoundItem.

Może być więcej odpowiednich zdarzeń niż te, o których wspomniałem, ale wpadłeś na pomysł.

7

Ty może narazić właściwości z INestedObj do wiązania, ale rozwiązanie jest bardzo messy.To dać jakieś tło, wszystkie WinForms kontrole, które wspierają wykorzystanie Databinding TypeDescriptor w celu określenia, które właściwości istnieć na obiektach są one wiążące dla . Poprzez TypeDescriptionProvider i CustomTypeDescriptor można zastąpić domyślne zachowanie, a tym samym dodać/ukryć właściwości - w tym przypadku ukrywając właściwość NestedObj i zastępując ją wszystkimi właściwościami typu zagnieżdżonego.

Technika mam zamiar pokazać ma 2 (Big-ish) Ostrzeżenia:

  1. Ponieważ pracujemy z interfejsami (a nie konkretnych klas), trzeba dodać deskryptora niestandardowego typu w środowisko wykonawcze.
  2. Deskryptor typu niestandardowego musi być w stanie utworzyć konkretną instancję o numerze IParentObj, dlatego musi znać jedną z takich klas, która ma domyślny konstruktor.

(proszę wybaczyć długie code)

Po pierwsze, trzeba sposobu owijania PropertyDescriptor z zagnieżdżonego typu tak, że może on być dostępny z rodzaju macierzystego:

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 
     ); 
    } 
} 

Następnie trzeba napisać deskryptor niestandardowy typ, który eksponuje właściwości z typu zagnieżdżonego:

public class ParentObjDescriptor : CustomTypeDescriptor { 
    public override PropertyDescriptorCollection GetProperties(
     Attribute[] attributes) { 
     PropertyDescriptorCollection properties 
      = new PropertyDescriptorCollection(null); 

     foreach (PropertyDescriptor outer in TypeDescriptor.GetProperties(
      new ParentObj() /* concrete implementation of IParentObj */, 
      attributes, true)) { 
      if (outer.PropertyType == typeof(INestedObj)) { 
       foreach (PropertyDescriptor inner in TypeDescriptor.GetProperties(
        typeof(INestedObj))) { 
        properties.Add(new InnerPropertyDescriptor(outer, 
         inner, attributes)); 
       } 
      } 
      else { 
       properties.Add(outer); 
      } 
     } 

     return properties; 
    } 
} 

... An d musisz sposób odsłaniając deskryptor z góry:

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

Wreszcie, w czasie wykonywania (przed wiążą się z DataGridView), należy skojarzyć z dostawcą Opis Typ z interfejsem IParentObj. Nie można tego zrobić podczas kompilacji, ponieważ TypeDescriptionProviderAttribute nie można umieścić na interfejsach ...

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

testowałem to poprzez wiązanie DataGridView do IParentObj[] i niskie i oto tworzy kolumny dla Prop1, Prop2 i NestedObj.Prop3.

Musisz zadać sobie pytanie, chociaż ... czy naprawdę jest to warte tego wysiłku?

+0

Wow, dziękuję Bradley. Dostajesz trofeum ukończenia. Ostatniej nocy udało mi się wypracować inne (prostsze ???) rozwiązanie, które opiszę obecnie. –