使用List<T>作为DataGridView数据源时,如何对其进行排序,其中T是匿名类型。

7

一个相对简单的问题。我有一个数据网格视图,它只显示统计信息。没有行的编辑/添加/删除操作。数据网格视图绑定到一个列表。我想要实现的就是让用户能够对列进行排序。

class Market
{
    public int Location {get;set;}
    public float Value {get;set;}
    //...
}
class City
{
    public String Name {get;set;}
    //...
}

List<Market> _markets;
List<City> _cities;
//Lists are populated.

dataGridView1.DataSource = _markets.Select(market => 
    new { _cities[market.Location].Name, market.Value}).ToList();

正如预期的那样,这些列不可排序,但所显示的信息是想要的。我的问题是如何使用最简单和最少的代码使DataGridView根据列类型进行排序,因为该代码将在多个地方使用。

此应用程序曾经使用具有视图的数据库。然后,这些视图填充了DataGridViews。这些视图仍然存在,因此可能的解决方案是:

DataBase.ViewMarketValue temp = new DataBase.ViewMarketValue()

_markets.ForEach(market => temp.AddViewMarketValueRow(_cities[market.Location].Name, market.Value);
dataGridView1.DataSource = temp;

这会得到期望的结果:一个包含所有信息且可排序的DataGridView。唯一的问题是在这方面使用视图似乎是错误的。那么我该怎么办?
2个回答

11
为了能够在 DataGridView 中自动排序数据,您需要一个实现 IBindingListView 接口的集合。在 BCL 中,唯一实现此接口的类是 DataViewBindingSource(但后者仅在基础数据源也支持排序时才支持排序)。
因此,您有几个选项:
  • 创建一个 DataTable 来存储数据,并将其绑定到 DataGridView(它实际上会绑定到 DataTableDefaultView 上)
  • 创建自己的实现了 IBindingListView 接口的集合类
  • 使用现有的实现,例如 Marc Gravell 在这篇帖子中发布的 AdvancedList<T> 类。您还需要添加一个构造函数来从查询结果中构建列表:

    public AdvancedList(IEnumerable<T> collection)
    {
        foreach (var item in collection)
        {
            Add(item);
        }
    }
    
    由于您的查询结果是匿名类型,因此您将无法直接调用构造函数。最简单的解决方法是利用类型推断,创建一个通用方法来创建列表。为方便起见,您可以将其创建为扩展方法:
    public static AdvancedList<T> ToAdvancedList<T>(this IEnumerable<T> source)
    {
        return new AdvancedList<T>(source);
    }
    

    您可以像这样使用它:

    dataGridView1.DataSource = _markets.Select(market => 
        new { _cities[market.Location].Name, market.Value}).ToAdvancedList();
    

非常好的解决方案! 我唯一的抱怨(而且这只是小问题)是排序不如理想的快,但它可以容忍并且是迄今为止最好的东西。 - Nick Babcock
1
它速度慢是因为它使用了反射来获取属性值。您可以轻松修改PropertyComparer类,使其在创建时生成一个委托,并使用该委托代替property.GetValue。 - Thomas Levesque
@ThomasLevesque 如果您能发布一些代码片段来实现您的建议,那将非常棒。谢谢。 - Esen
实现了来自Marc Gravell帖子的AdvancedList<T>。我一直在更新现有应用程序,将数据绑定从旧式数据表更改为从SQL结果集反序列化的类,并且这解决了从数据表切换数据源到类的ILIST<T>时出现排序丢失的问题。 - Gary Kindel

3
这是我根据 Thomas Levesque (https://dev59.com/V1PTa4cB1Zd3GeqPjG1M#4702631) 和 Mary Hamlin (https://dev59.com/I3RB5IYBdhLWcg3wpYmo#5805044) 的答案所做的事情:
1)在我的静态类中添加了一个扩展方法,名为 Funcs
static class Funcs
{
    public static DataTable ToDataTable<T>(this IList<T> data)
    {
        PropertyDescriptorCollection properties = 
            TypeDescriptor.GetProperties(typeof(T));
        DataTable table = new DataTable();
        foreach (PropertyDescriptor prop in properties)
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
        foreach (T item in data)
        {
            DataRow row = table.NewRow();
            foreach (PropertyDescriptor prop in properties)
                 row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
            table.Rows.Add(row);
        }
        return table;
    }
}

"2) 然后像这样使用它:"
var query = _markets.Select(market => 
    new { _cities[market.Location].Name, market.Value}).ToList();
dataGridView1.DataSource = Funcs.ToDataTable(query);

终于有一个简单的解决方案了! - TK-421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接