【问题标题】:Binding DataGrid to ObservableCollection<Dictionary>将 DataGrid 绑定到 ObservableCollection<Dictionary>
【发布时间】:2012-12-19 17:10:08
【问题描述】:

我有一个ObservableCollection&lt;Dictionary&gt;,想将它绑定到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 不起作用。

【问题讨论】:

  • DataGrid 根本不支持这个。如果您需要动态列,还有其他方法。
  • @HenkHolterman 我确实需要动态列。你能指点我其他的方法吗?
  • 在您的示例中,您似乎正在将人员添加到网格中。我假设您需要动态列,因为您需要在其他时间在同一网格中显示其他内容而不是人员?如果是这样,是否可以为您需要显示的所有其他项目创建一个Person 类和类似的类(而不是使用Dictionary&lt;&gt;)?
  • @Sphinxxx 是的,你是对的。实际上是测试结果 - 多个测试按顺序运行,当前运行的测试结果将显示在数据网格中。我考虑为每个测试结果添加一个结果类。我对如何根据正在运行的测试更新数据绑定感到困惑。我不想在视图中添加 select 语句来选择要绑定的适当对象 - 因为这需要我在每次添加新测试时更新它。我认为这不是一个好的设计。有更好的处理方法吗?
  • 你真的需要不同的课程来进行不同的测试吗?您能否通过添加此类示例来更新您的帖子?

标签: c# wpf data-binding dictionary


【解决方案1】:

您可以使用可绑定的动态字典。这会将每个字典条目作为属性公开。

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

【讨论】:

  • BindableDynamicDictionary 非常适合我的要求。感谢 cmets 的示例。
  • 为什么在RaisePropertyChanged中使用局部变量propChange,在dataGrid1_AutoGeneratedColumns()中使用names
  • @Sinatr 对它们没有要求。我想我当时认为他们使它更具可读性。
  • 只是想知道如果简单地将字典键类型从对象更改为某种实现 IPropertyChanged 的​​类型不能解决这个问题?所以每个项目都将通过 IPropertyChanged 进行连接
  • @sll 好吧,在字典中更改值的键是个坏主意,所以我不确定它是如何工作的。
【解决方案2】:

根据韦斯顿的回答,我想出了另一个解决方案,但不使用自定义的 BindableDynamicDictionary 类。

在命名空间System.Dynamic(在 ASP.NET 中大量使用)中有一个名为 ExpandoObject 的类。

它基本上与 westons BindableDynamicDictionary 做同样的事情,但缺点是没有索引运算符可用,因为它显式实现了接口 IDictionary&lt;string, object&gt;

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 强制转换为 IDictionary&lt;string, object&gt; 才能通过索引运算符访问/添加值或属性。

【讨论】:

    猜你喜欢
    • 2013-09-07
    • 2012-03-09
    • 2016-09-13
    • 2012-04-02
    • 1970-01-01
    • 2013-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多