Associazione di DataGrid a ObservableCollection

Ho un ObservableCollection e voglio associarlo 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); 

Volevo che le chiavi diventassero l’intestazione di DataGrid e che i valori di ciascun elemento del Dictionary fossero le righe. L’impostazione di ItemsSource non funziona.

Potresti usare un dizionario dinamico bindable. Questo esporrà ogni voce del dizionario come una proprietà.

 ///  /// 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)); } } 

Quindi puoi usarlo in questo modo:

  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 ha bisogno di un codice personalizzato per build le colonne:

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) }); } } 

In base alla risposta di Weston, ho trovato un’altra soluzione senza utilizzare una class personalizzata BindableDynamicDictionary.

Esiste una class chiamata ExpandoObject nello spazio dei nomi System.Dynamic (che è molto utilizzato in ASP.NET).

Fondamentalmente fa la stessa cosa di weston BindableDynamicDictionary con lo svantaggio di non avere l’operatore index disponibile poiché implementa esplicitamente l’interfaccia 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) }); } } 

Nota che l’unica differenza qui è che devi lanciare ExpandoObject su IDictionary per accedere / aggiungere valori o proprietà tramite l’operatore index.