Привязка DataGrid к ObservableCollection
у меня есть ObservableCollection<Dictionary>
и хотите привязать его к DataGrid
.
ObservableDictionary<String,Object> NewRecord1 = new ObservableDictionary<string,object>();
Dictionary<String,Object> Record1 = new Dictionary<string,object>();
Record1.Add("FirstName", "FName1");
Record1.Add("LastName", "LName1");
Record1.Add("Age", "32");
DictRecords.Add(Record1);
Dictionary<String, Object> Record2 = new Dictionary<string, object>();
NewRecord2.Add("FirstName", "FName2");
NewRecord2.Add("LastName", "LName2");
NewRecord2.Add("Age", "42");
DictRecords.Add(Record2);
Я хотел, чтобы ключи стали заголовком DataGrid
и значения каждого Dictionary
элемент строки. Установка ItemsSource
не работает.
2 ответов
вы можете использовать связываемый динамический словарь. Это откроет каждую запись словаря как свойство.
/// <summary>
/// Bindable dynamic dictionary.
/// </summary>
public sealed class BindableDynamicDictionary : DynamicObject, INotifyPropertyChanged
{
/// <summary>
/// The internal dictionary.
/// </summary>
private readonly Dictionary<string, object> _dictionary;
/// <summary>
/// Creates a new BindableDynamicDictionary with an empty internal dictionary.
/// </summary>
public BindableDynamicDictionary()
{
_dictionary = new Dictionary<string, object>();
}
/// <summary>
/// Copies the contents of the given dictionary to initilize the internal dictionary.
/// </summary>
/// <param name="source"></param>
public BindableDynamicDictionary(IDictionary<string, object> source)
{
_dictionary = new Dictionary<string, object>(source);
}
/// <summary>
/// You can still use this as a dictionary.
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public object this[string key]
{
get
{
return _dictionary[key];
}
set
{
_dictionary[key] = value;
RaisePropertyChanged(key);
}
}
/// <summary>
/// This allows you to get properties dynamically.
/// </summary>
/// <param name="binder"></param>
/// <param name="result"></param>
/// <returns></returns>
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
return _dictionary.TryGetValue(binder.Name, out result);
}
/// <summary>
/// This allows you to set properties dynamically.
/// </summary>
/// <param name="binder"></param>
/// <param name="value"></param>
/// <returns></returns>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
_dictionary[binder.Name] = value;
RaisePropertyChanged(binder.Name);
return true;
}
/// <summary>
/// This is used to list the current dynamic members.
/// </summary>
/// <returns></returns>
public override IEnumerable<string> 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));
}
}
тогда вы можете использовать его следующим образом:
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<object>();
collection.Add(person);
dataGrid1.ItemsSource = collection;
}
Datagrid нужен пользовательский код для построения столбцов:
XAML:
<DataGrid AutoGenerateColumns="True" Name="dataGrid1" AutoGeneratedColumns="dataGrid1_AutoGeneratedColumns" />
событие AutoGeneratedColumns:
private void dataGrid1_AutoGeneratedColumns(object sender, EventArgs e)
{
var dg = sender as DataGrid;
var first = dg.ItemsSource.Cast<object>().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) });
}
}
на основе ответа westons я придумал другое решение без использования пользовательского класса BindableDynamicDictionary.
есть класс под названием ExpandoObject
в пространстве имен System.Dynamic
(который тяжело использован внутри ASP.NET).
он в основном делает то же самое, что и westons BindableDynamicDictionary с недостатком отсутствия оператора индекса, поскольку он явно реализует интерфейс IDictionary<string, object>
private void MyDataGrid_AutoGeneratedColumns(object sender, EventArgs e)
{
var dg = sender as DataGrid;
dg.Columns.Clear();
var first = dg.ItemsSource.Cast<object>().FirstOrDefault() as IDictionary<string, object>;
if (first == null) return;
var names = first.Keys;
foreach (var name in names)
{
dg.Columns.Add(new DataGridTextColumn { Header = name, Binding = new Binding(name) });
}
}
обратите внимание, что единственная разница здесь что вы должны бросить ExpandoObject
to IDictionary<string, object>
для доступа/добавления значений или свойств через оператор индекса.