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
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
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
Nota che l’unica differenza qui è che devi lanciare ExpandoObject
su IDictionary
per accedere / aggiungere valori o proprietà tramite l’operatore index.