2009-05-19 17 views
26

Ho una serie di "dati dinamici" che devo associare a GridControl. Fino ad ora, ho utilizzato la classe DataTable standard che fa parte dello spazio dei nomi System.Data. Questo ha funzionato bene, ma mi è stato detto che non posso usare questo dato che è troppo pesante per la serializzazione attraverso la rete tra il client & server.Dati dinamici per l'associazione dei dati

Quindi ho pensato di poter facilmente replicare una versione "tagliata" della classe DataTable semplicemente con un tipo di List<Dictionary<string, object>> in cui l'elenco rappresenta la raccolta di righe e ogni dizionario rappresenta una riga con i nomi e i valori delle colonne come un tipo KeyValuePair. Ho potuto impostare la griglia di avere le proprietà della colonna DataField per adeguarli a quelli delle chiavi nel Dizionario (proprio come stavo facendo per i nomi delle colonne del DataTable.

Tuttavia dopo aver fatto

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

La griglia ha non ci sono dati ...

penso di aver bisogno di implementare IEnumerator - qualsiasi aiuto su questo sarebbe molto apprezzato

Esempio codice chiamante è simile al seguente:!

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? Intendi DataGridView? –

risposta

61

Benvenuti nel meraviglioso mondo di System.ComponentModel. Questo angolo oscuro di .NET è molto potente, ma molto complesso.

Una parola di cautela; a meno che tu non abbia un sacco di tempo per questo - potresti fare semplicemente la serializzazione in qualsiasi meccanismo tu sia soddisfatto, ma reidratarlo di nuovo in uno DataTable a ciascuna estremità ... ciò che segue non è per i deboli di cuore; p

in primo luogo - l'associazione di dati (per le tabelle) funziona contro liste (IList/IListSource) - così List<T> dovrebbe andare bene (a cura: ho letto male qualcosa). Ma non capirai che il tuo dizionario è in realtà colonne ...

Per ottenere un tipo per far finta di avere colonne è necessario utilizzare le implementazioni personalizzate PropertyDescriptor. Esistono diversi modi per eseguire questa operazione, a seconda che le definizioni di colonna siano sempre uguali (ma determinate in fase di esecuzione, ad esempio forse da configurazione) o se cambia per utilizzo (ad esempio, come ogni istanza di DataTable può avere colonne diverse).

Per la personalizzazione "per esempio", è necessario guardare a ITypedList - questa bestia (implementato in oltre -IList) ha il compito divertimento di presentare le proprietà per i dati tabulari ... ma non è il solo:

per la personalizzazione "per tipo", si può guardare a TypeDescriptionProvider - questo può suggerire proprietà dinamiche per una classe di ...

... oppure è possibile implementare ICustomTypeDescriptor - ma questo è usato solo (per gli elenchi) in molto circostanze occasionali (un indicizzatore di oggetti (public object this[int index] {get;} ") e almeno una riga nell'elenco al punto di associazione). (questa interfaccia è molto più utile quando si vincolano oggetti discreti, cioè non elenchi).

Implementare ITypedList e fornire un modello PropertyDescriptor è un lavoro duro ... quindi è fatto solo occasionalmente. Conosco abbastanza bene, ma non lo farei solo per le risate ...


Ecco un molto, molto semplificata implementazione (tutte le colonne sono stringhe; notifiche (via descrittore), non convalida (IDataErrorInfo), senza conversioni (TypeConverter), nessun supporto ulteriore elenco (IBindingList/IBindingListView) , nessuna astrazione (IListSource), nessun altro altro metadata/attributi, ecc.):

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

Risposta stupenda Farò un tentativo per farti sapere come faccio ... –

+3

Ahi ... I miei occhi stanno sanguinando. Torno ai set di dati e alle raccolte di colonne dinamiche: D – Larry

+1

@ControlBreak - lol; una scelta saggia, sospetto. –