【问题标题】:WinForms DataGridView - databind to an object with a list property (variable number of columns)WinForms DataGridView - 数据绑定到具有列表属性的对象(列数可变)
【发布时间】:2011-06-10 14:34:37
【问题描述】:

我有一个我想在 DataGridView 中显示的 .NET 类,并且默认数据绑定 - 将 DGV 的 DataSource 设置为对象 - 产生了我 90% 的要求(即它正确输出公共属性,我可以添加轻松排序)。

但是,我需要绑定的属性之一是 List,其中包含需要位于其他数据绑定项之后的单独列中的数据。我不知道如何最好地实现这一点。

我的班级看起来像这样:

public class BookDetails
{
    public string Title { get; set; }
    public int TotalRating { get; set; }
    public int Occurrence { get; set; }
    public List<int> Rating { get; set; }
}

理想情况下,我可以将该 Rating 属性扩展为多个数字列,以便在运行时提供如下输出:

标题 |总评分 |发生 | R1 | R2 | R3 ... RN

将总评分计算为所有单个评分的总和也会很有用,但我目前正在手动更新它,没有问题。

【问题讨论】:

  • 您将不得不为该类型实现一个 TypeDescriptor(或者可能是 TypeConverter)。如果您知道该怎么做,那就很简单了。不幸的是,我写的一个很好的例子现在正在起作用。
  • @leppie - TypeConverter 不适用于此处;实际上,ITypedList 可能是最简单的;之后 - TypeDescriptionProvider(因为它不会将 ICustomTypeDescriptor 用于类型化列表)
  • @leppie - 我们一定是我认识的仅有的两个傻瓜,他们疯到了要搞砸框架的这个黑暗角落;p
  • (您也可以将其设为读写,但这会有点混乱,因为您需要知道每行列表的长度;有点痛苦......)
  • @Marc Gravell:呵呵 :) IIRC,我的解决方案只是继承自 BindingSource。似乎和你的答案一样。

标签: c# .net winforms data-binding datagridview


【解决方案1】:

像这样?

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
public class BookDetails
{
    public string Title { get; set; }
    public int TotalRating { get; set; }
    public int Occurrence { get; set; }
    public List<int> Rating { get; set; }
}
class BookList : List<BookDetails>, ITypedList
{

    public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
    {
        var origProps = TypeDescriptor.GetProperties(typeof(BookDetails));
        List<PropertyDescriptor> newProps = new List<PropertyDescriptor>(origProps.Count);
        PropertyDescriptor doThisLast = null;
        foreach (PropertyDescriptor prop in origProps)
        {

            if (prop.Name == "Rating") doThisLast = prop;
            else newProps.Add(prop);
        }
        if (doThisLast != null)
        {
            var max = (from book in this
                       let rating = book.Rating
                       where rating != null
                       select (int?)rating.Count).Max() ?? 0;
            if (max > 0)
            {
                // want it nullable to account for jagged arrays
                Type propType = typeof(int?); // could also figure this out from List<T> in
                                              // the general case, but make it nullable
                for (int i = 0; i < max; i++)
                {
                    newProps.Add(new ListItemDescriptor(doThisLast, i, propType));
                }
            }
        }
        return new PropertyDescriptorCollection(newProps.ToArray());
    }

    public string GetListName(PropertyDescriptor[] listAccessors)
    {
        return "";
    }
}
class ListItemDescriptor : PropertyDescriptor
{
    private static readonly Attribute[] nix = new Attribute[0];
    private readonly PropertyDescriptor tail;
    private readonly Type type;
    private readonly int index;
    public ListItemDescriptor(PropertyDescriptor tail, int index, Type type) : base(tail.Name + "[" + index + "]", nix)
    {
        this.tail = tail;
        this.type = type;
        this.index = index;
    }
    public override object GetValue(object component)
    {
        IList list = tail.GetValue(component) as IList;
        return (list == null || list.Count <= index) ? null : list[index];
    }
    public override Type PropertyType
    {
        get { return type; }
    }
    public override bool IsReadOnly
    {
        get { return true; }
    }
    public override void SetValue(object component, object value)
    {
        throw new NotSupportedException();
    }
    public override void ResetValue(object component)
    {
        throw new NotSupportedException();
    }
    public override bool CanResetValue(object component)
    {
        return false;
    }
    public override Type ComponentType
    {
        get { return tail.ComponentType; }
    }
    public override bool ShouldSerializeValue(object component)
    {
        return false;
    }
}
static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        var data = new BookList {
            new BookDetails { Title = "abc", TotalRating = 3, Occurrence = 2, Rating = new List<int> {1,2,1}},
            new BookDetails { Title = "def", TotalRating = 3, Occurrence = 2, Rating = null },
            new BookDetails { Title = "ghi", TotalRating = 3, Occurrence = 2, Rating = new List<int> {3, 2}},
            new BookDetails { Title = "jkl", TotalRating = 3, Occurrence = 2, Rating = new List<int>()},
        };
        Application.Run(new Form
        {
            Controls = {
                new DataGridView {
                    Dock = DockStyle.Fill,
                    DataSource = data
                }
            }
        });

    }
}

【讨论】:

  • (我还忽略了整个“列表访问器”的内容 - 如果您需要,它只是通过链跟踪每个调用的情况)跨度>
  • +1 很酷,比我想象的要复杂一些,因为列表中的项目数量可变:)
  • 这个解决方案非常有趣。我将不得不调查 ITypedList 和整个 PropertyDescriptor/TypeDescriptor 字段。我正在调试器中运行代码并学到了很多东西,非常感谢:)
  • 非常有用和简洁。谢谢你。
  • 优秀的解决方案,它提供了一个很好的例子。谢谢。
猜你喜欢
  • 2011-01-27
  • 2010-10-10
  • 1970-01-01
  • 1970-01-01
  • 2013-09-24
  • 2011-02-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多