2009-05-19 19 views
26

Mam zestaw "danych dynamicznych", które muszę powiązać z GridControl. Do tej pory używałem standardowej klasy DataTable, która jest częścią przestrzeni nazw System.Data. To działa dobrze, ale powiedziano mi, że nie mogę tego użyć, ponieważ jest zbyt ciężki do serializacji w sieci między serwerem klienta &.Dane dynamiczne wiązania danych

Pomyślałem więc, że mogę łatwo replikować "skróconą" wersję klasy DataTable, po prostu mając typ List<Dictionary<string, object>>, przy czym lista reprezentuje kolekcję wierszy, a każdy słownik reprezentuje jeden wiersz z nazwami i wartościami kolumn jako typ KeyValuePair. Mógłbym założyć siatki, aby mieć właściwości kolumna dataField pasujące do tych kluczy w słowniku (tak jak robiłem dla nazw kolumn DataTable za.

Jednak po wykonaniu

gridControl.DataSource = table; 
gridControl.RefreshDataSource(); 

Siatka ma brak danych ...

Chyba muszę wdrożyć IEnumerator - każda pomoc w tej sprawie byłby znacznie doceniane

Przykład kodu wywołanie wygląda następująco:

var table = new List<Dictionary<string,object>>(); 

var row = new Dictionary<string, object> 
{ 
    {"Field1", "Data1"}, 
    {"Field2", "Data2"}, 
    {"Field3", "Data3"} 
}; 

table.Add(row); 

gridControl1.DataSource = table; 
gridControl1.RefreshDataSource(); 
+0

GridControl? Czy masz na myśli DataGridView? –

Odpowiedz

61

Witamy w cudownym świecie System.ComponentModel. Ten ciemny zakątek .NET jest bardzo potężny, ale bardzo złożony.

Słowo ostrożności; chyba że masz na to dużo czasu - możesz zrobić to dobrze, po prostu serializować go w jakimkolwiek mechanizmie, z którego jesteś zadowolony, ale z powrotem nawodnić go z powrotem na DataTable na każdym końcu ... co następuje nie jest dla osób o słabych nerwach; p

pierwsze - wiązania danych (tabel) działa przeciwko list (IList/IListSource) - tak List<T> powinny być dobre (edycja: I misread coś). Ale nie zrozumiemy, że twój słownik to kolumny ...

Aby uzyskać typ, który będzie udawał kolumny, musisz użyć niestandardowych implementacji PropertyDescriptor. Istnieje kilka sposobów, aby to zrobić, w zależności od tego, czy definicje kolumn są zawsze takie same (ale określane w czasie wykonywania, tj. Prawdopodobnie z konfiguracji), czy też zmieniają się w zależności od użycia (tak, jak każda instancja DataTable może mieć różne kolumny).

Dla „na przykład” personalizacji, trzeba spojrzeć na ITypedList - tej bestii (realizowany w Ponadto do IList) ma za zadanie zabawy prezentuje właściwości dla danych tabelarycznych ... ale to nie jest sam:

For „za typ” personalizacji, można spojrzeć na TypeDescriptionProvider - może to sugerować właściwości dynamiczne dla klasy ...

... czy można wdrożyć ICustomTypeDescriptor - ale ten jest używany tylko (na listach) w bardzo okoliczności okazjonalne (indeksowanie obiektów (public object this[int index] {get;} ") i co najmniej jeden wiersz na liście w punkcie wiązania). (ten interfejs jest o wiele bardziej przydatny przy wiązaniu dyskretnych obiektów - tzn. nie list).

Wdrażanie ITypedList i dostarczanie modelu PropertyDescriptor to ciężka praca ... dlatego jest wykonywana tylko bardzo rzadko. Znam go dość dobrze, ale nie zrobiłbym tego tylko dla śmiechu ...


Oto bardzo uproszczony realizacja (wszystkie kolumny są ciągi, bez powiadomienia (przez deskryptor), brak walidacji (IDataErrorInfo), bez konwersji (TypeConverter), bez dodatkowego wsparcia lista (IBindingList/IBindingListView) , bez abstrakcji (IListSource), żadnych innych innych metadanych/atrybutów itp.):

using System.ComponentModel; 
using System.Collections.Generic; 
using System; 
using System.Windows.Forms; 

static class Program 
{ 
    [STAThread] 
    static void Main() 
    { 
     Application.EnableVisualStyles(); 
     PropertyBagList list = new PropertyBagList(); 
     list.Columns.Add("Foo"); 
     list.Columns.Add("Bar"); 
     list.Add("abc", "def"); 
     list.Add("ghi", "jkl"); 
     list.Add("mno", "pqr"); 

     Application.Run(new Form { 
      Controls = { 
       new DataGridView { 
        Dock = DockStyle.Fill, 
        DataSource = list 
       } 
      } 
     }); 
    } 
} 
class PropertyBagList : List<PropertyBag>, ITypedList 
{ 
    public PropertyBag Add(params string[] args) 
    { 
     if (args == null) throw new ArgumentNullException("args"); 
     if (args.Length != Columns.Count) throw new ArgumentException("args"); 
     PropertyBag bag = new PropertyBag(); 
     for (int i = 0; i < args.Length; i++) 
     { 
      bag[Columns[i]] = args[i]; 
     } 
     Add(bag); 
     return bag; 
    } 
    public PropertyBagList() { Columns = new List<string>(); } 
    public List<string> Columns { get; private set; } 

    PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors) 
    { 
     if(listAccessors == null || listAccessors.Length == 0) 
     { 
      PropertyDescriptor[] props = new PropertyDescriptor[Columns.Count]; 
      for(int i = 0 ; i < props.Length ; i++) 
      { 
       props[i] = new PropertyBagPropertyDescriptor(Columns[i]); 
      } 
      return new PropertyDescriptorCollection(props, true);    
     } 
     throw new NotImplementedException("Relations not implemented"); 
    } 

    string ITypedList.GetListName(PropertyDescriptor[] listAccessors) 
    { 
     return "Foo"; 
    } 
} 
class PropertyBagPropertyDescriptor : PropertyDescriptor 
{ 
    public PropertyBagPropertyDescriptor(string name) : base(name, null) { } 
    public override object GetValue(object component) 
    { 
     return ((PropertyBag)component)[Name]; 
    } 
    public override void SetValue(object component, object value) 
    { 
     ((PropertyBag)component)[Name] = (string)value; 
    } 
    public override void ResetValue(object component) 
    { 
     ((PropertyBag)component)[Name] = null; 
    } 
    public override bool CanResetValue(object component) 
    { 
     return true; 
    } 
    public override bool ShouldSerializeValue(object component) 
    { 
     return ((PropertyBag)component)[Name] != null; 
    } 
    public override Type PropertyType 
    { 
     get { return typeof(string); } 
    } 
    public override bool IsReadOnly 
    { 
     get { return false; } 
    } 
    public override Type ComponentType 
    { 
     get { return typeof(PropertyBag); } 
    } 
} 
class PropertyBag 
{ 
    private readonly Dictionary<string, string> values 
     = new Dictionary<string, string>(); 
    public string this[string key] 
    { 
     get 
     { 
      string value; 
      values.TryGetValue(key, out value); 
      return value; 
     } 
     set 
     { 
      if (value == null) values.Remove(key); 
      else values[key] = value; 
     } 
    } 
} 
+0

Niesamowita odpowiedź. Podpowiem, jak się dostanę ... –

+3

Ouy ... Moje oczy krwawią. Wracam do zbiorów danych i kolumn dynamicznych: D – Larry

+1

@ControlBreak - lol; mądry wybór, podejrzewam. –

Powiązane problemy