Enlazando DataGrid a ObservableCollection

Tengo un ObservableCollection y quiero vincularlo a un DataGrid .

 ObservableDictionary NewRecord1 = new ObservableDictionary(); Dictionary Record1 = new Dictionary(); Record1.Add("FirstName", "FName1"); Record1.Add("LastName", "LName1"); Record1.Add("Age", "32"); DictRecords.Add(Record1); Dictionary Record2 = new Dictionary(); NewRecord2.Add("FirstName", "FName2"); NewRecord2.Add("LastName", "LName2"); NewRecord2.Add("Age", "42"); DictRecords.Add(Record2); 

Quería que las claves se convirtieran en el encabezado de DataGrid y que los valores de cada elemento de Dictionary fueran las filas. Configurar ItemsSource no funciona.

Podría usar un diccionario dynamic enlazable. Esto expondrá cada entrada del diccionario como una propiedad.

 ///  /// Bindable dynamic dictionary. ///  public sealed class BindableDynamicDictionary : DynamicObject, INotifyPropertyChanged { ///  /// The internal dictionary. ///  private readonly Dictionary _dictionary; ///  /// Creates a new BindableDynamicDictionary with an empty internal dictionary. ///  public BindableDynamicDictionary() { _dictionary = new Dictionary(); } ///  /// Copies the contents of the given dictionary to initilize the internal dictionary. ///  ///  public BindableDynamicDictionary(IDictionary source) { _dictionary = new Dictionary(source); } ///  /// You can still use this as a dictionary. ///  ///  ///  public object this[string key] { get { return _dictionary[key]; } set { _dictionary[key] = value; RaisePropertyChanged(key); } } ///  /// This allows you to get properties dynamically. ///  ///  ///  ///  public override bool TryGetMember(GetMemberBinder binder, out object result) { return _dictionary.TryGetValue(binder.Name, out result); } ///  /// This allows you to set properties dynamically. ///  ///  ///  ///  public override bool TrySetMember(SetMemberBinder binder, object value) { _dictionary[binder.Name] = value; RaisePropertyChanged(binder.Name); return true; } ///  /// This is used to list the current dynamic members. ///  ///  public override IEnumerable GetDynamicMemberNames() { return _dictionary.Keys; } public event PropertyChangedEventHandler PropertyChanged; private void RaisePropertyChanged(string propertyName) { var propChange = PropertyChanged; if (propChange == null) return; propChange(this, new PropertyChangedEventArgs(propertyName)); } } 

Entonces puedes usarlo así:

  private void testButton1_Click(object sender, RoutedEventArgs e) { // Creating a dynamic dictionary. var dd = new BindableDynamicDictionary(); //access like any dictionary dd["Age"] = 32; //or as a dynamic dynamic person = dd; // Adding new dynamic properties. // The TrySetMember method is called. person.FirstName = "Alan"; person.LastName = "Evans"; //hacky for short example, should have a view model and use datacontext var collection = new ObservableCollection(); collection.Add(person); dataGrid1.ItemsSource = collection; } 

Datagrid necesita un código personalizado para construir las columnas:

XAML:

  

Evento AutoGeneratedColumns:

  private void dataGrid1_AutoGeneratedColumns(object sender, EventArgs e) { var dg = sender as DataGrid; var first = dg.ItemsSource.Cast().FirstOrDefault() as DynamicObject; if (first == null) return; var names = first.GetDynamicMemberNames(); foreach(var name in names) { dg.Columns.Add(new DataGridTextColumn { Header = name, Binding = new Binding(name) }); } } 

Basado en la respuesta de Weston, se me ocurrió otra solución sin utilizar una clase personalizada BindableDynamicDictionary.

Hay una clase llamada ExpandoObject en el espacio de nombres System.Dynamic (que se usa mucho en ASP.NET).

Básicamente hace lo mismo que westons BindableDynamicDictionary con el inconveniente de no tener el operador de índice disponible ya que implementa explícitamente la interfaz IDictionary

 private void MyDataGrid_AutoGeneratedColumns(object sender, EventArgs e) { var dg = sender as DataGrid; dg.Columns.Clear(); var first = dg.ItemsSource.Cast().FirstOrDefault() as IDictionary; if (first == null) return; var names = first.Keys; foreach (var name in names) { dg.Columns.Add(new DataGridTextColumn { Header = name, Binding = new Binding(name) }); } } 

Tenga en cuenta que la única diferencia aquí es que debe ExpandoObject el ExpandoObject a IDictionary para acceder / agregar valores o propiedades a través del operador de índice.