2009-05-19 10 views
26

Tengo un conjunto de 'datos dinámicos' 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 funcionó bien, pero me dijeron que no puedo usarlo ya que es demasiado pesado para la serialización en la red entre el servidor &.Datos dinámicos de enlace de datos

Así que pensé que podría replicar fácilmente una versión 'reducida' de la clase DataTable simplemente teniendo un tipo de List<Dictionary<string, object>> donde la lista representa la colección de filas, y cada diccionario representa una fila con los nombres y valores de las columnas como un tipo KeyValuePair. Podría configurar la red para tener las propiedades de columna DataField para que coincida con los de las teclas en el diccionario (al igual que estaba haciendo para los nombres de las columnas del DataTable.

Sin embargo después de hacer

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

La rejilla tiene no hay datos ...

creo que es necesario para poner en práctica IEnumerator - cualquier ayuda en esto sería muy apreciada

Ejemplo código de llamada es el siguiente:

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? ¿Te refieres a DataGridView? –

Respuesta

61

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 bien simplemente serializarlo en cualquier mecanismo que le satisfaga, pero rehidratarlo de nuevo en un DataTable en cada extremo ... lo que sigue no es para los débiles de corazón; p

en primer lugar - de enlace de datos (por tablas) va en contra de las listas (IList/IListSource) - por lo que debe estar bien List<T> (editado: leí mal algo). Pero no va a entender que su diccionario es en realidad columnas ...

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

Para la personalización "por ejemplo", usted tiene que mirar a ITypedList - esta bestia (implementado en Además a IList) tiene la tarea divertida de presentar propiedades para los datos tabulares ... pero no es la única:

para la personalización "según el tipo", se puede ver en TypeDescriptionProvider - esto puede sugerir propiedades dinámicas para una clase ...

... o se puede poner en práctica ICustomTypeDescriptor - pero esto se utiliza solamente (para las listas) en muy circunstancias ocasionales (un indexador de objetos ()") 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 PropertyDescriptor es un trabajo duro ... 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 muy, muy simplificada aplicación (todas las columnas son cadenas; ninguna notificación (vía descriptor), ninguna validación (IDataErrorInfo), no hay conversiones (TypeConverter), no hay soporte lista adicional (IBindingList/IBindingListView) , sin abstracción (IListSource), ningún otro metadata/atributo, 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<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

Respuesta impresionante Voy a darle una oportunidad, deja que sepas cómo me llevo ... –

+3

Ouch ... Mis ojos están sangrando. Vuelvo a conjuntos de datos y colecciones de columnas dinámicas: D – Larry

+1

@ControlBreak - lol; una sabia elección, sospecho. –