Datos dynamics de enlace de datos

Tengo un conjunto de ‘datos dynamics’ que necesito vincular a GridControl. Hasta ahora, he estado usando la clase estándar DataTable que es parte del espacio de nombres System.Data. Esto ha funcionado bien, pero me han dicho que no puedo usar esto, ya que es demasiado pesado para la serialización en la red entre el cliente y el servidor.

Así que pensé que podría replicar fácilmente una versión ‘reducida’ de la clase DataTable simplemente teniendo un tipo de List<Dictionary> donde la Lista representa la colección de filas, y cada Diccionario representa una fila con la nombres de columna y valores como un tipo KeyValuePair. Podría configurar la Grilla para que las propiedades de DataField de la columna coincidan con las de las claves en el Diccionario (tal como lo hacía con los nombres de columna de DataTable).

Sin embargo después de hacer

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

La grilla no tiene datos …

Creo que necesito implementar IEnumerator , ¡cualquier ayuda sobre esto sería muy apreciada!

El código de llamada de ejemplo se ve así:

 var table = new List<Dictionary>(); var row = new Dictionary { {"Field1", "Data1"}, {"Field2", "Data2"}, {"Field3", "Data3"} }; table.Add(row); gridControl1.DataSource = table; gridControl1.RefreshDataSource(); 

Bienvenido al maravilloso mundo de System.ComponentModel. Este rincón oscuro de .NET es muy poderoso, pero muy complejo.

Una palabra de precaución; a menos que tenga mucho tiempo para esto, puede hacerlo simplemente serializarlo en cualquier mecanismo que le satisfaga, pero rehidratarlo de nuevo a una DataTable en cada extremo … lo que sigue no es para los débiles de corazón; pag

En primer lugar, el enlace de datos (para las tablas) funciona en contra de las listas ( IList / IListSource ), por lo que List debería estar bien (editado: he leído mal algo). Pero no va a entender que tu diccionario es en realidad columnas …

Para hacer que un tipo pretenda tener columnas, necesita usar implementaciones personalizadas de PropertyDescriptor . Hay varias maneras de hacerlo, dependiendo de si las definiciones de columna son siempre las mismas (pero determinadas en tiempo de ejecución, es decir, quizás de la configuración), o si cambia según el uso (como que cada instancia de DataTable puede tener columnas diferentes).

Para la personalización “por instancia”, debe mirar a ITypedList : esta bestia (implementada además de IList ) tiene la divertida tarea de presentar propiedades para datos tabulares … pero no está sola:

Para la personalización “por tipo”, puede mirar TypeDescriptionProvider ; esto puede sugerir propiedades dinámicas para una clase …

… o puede implementar ICustomTypeDescriptor , pero esto solo se usa (para listas) en circunstancias muy ocasionales (un indexador de objetos ( public object this[int index] {get;} “) y al menos una fila en la lista en el punto de enlace). (esta interfaz es mucho más útil cuando se vinculan objetos discretos, es decir, no listas).

Implementar ITypedList y proporcionar un modelo de PropertyDescriptor es un trabajo difícil … por lo tanto, solo se realiza muy ocasionalmente. Estoy bastante familiarizado con eso, pero no lo haría solo por las risas …


Aquí hay una implementación muy, muy simplificada (todas las columnas son cadenas, no hay notificaciones (a través del descriptor), no hay validación ( IDataErrorInfo ), no hay conversiones ( TypeConverter ), no hay soporte de lista adicional ( IBindingList / IBindingListView ), no abstraction ( IListSource ), no other otros metadatos / atributos, etc.):

 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, 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(); } public List 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 values = new Dictionary(); 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; } } }