【问题标题】:Make all datagridview columns sortable使所有 datagridview 列可排序
【发布时间】:2016-04-28 22:54:31
【问题描述】:

我有一个带有datagridview 的 Windows 窗体。

理想情况:

用户点击九列中的任何一列,程序对所有数据进行排序,如果点击的列包含数字,我想要顶部的最小数字。如果单击的列包含一个字符串,我希望它按字母顺序(A-Z)排序。

我现在拥有的:

我在 Stack Overflow 上看到一个老问题,其中 OP 如何在单击“a”标题时对 datagridview 进行排序。与我的不同之处在于我希望我的 datagridview 可以按九列中的任何一列排序。

我有这个代码,从我发现的问题中偷来的:

dataGridView2.DataSource = listPlayers.Select(s => new { voornaam = s.Voornaam, 
                                                        Achternaam = s.Achternaam, 
                                                        positie = s.Positie, 
                                                        Nationaltieit = s.Nationaliteit, 
                                                        Leeftijd = s.Age, 
                                                        Aanval = s.Aanval, 
                                                        Verdediging = s.Verdediging, 
                                                        Gemiddeld = s.Gemiddeld, 
                                                        waarde = s.TransferWaarde })
                                   .OrderBy(s => s.Achternaam)
                                   .ToList();

foreach(DataGridViewColumn column in dataGridView2.Columns)
    {
        dataGridView2.Columns[column.Name].SortMode =
                                  DataGridViewColumnSortMode.Automatic;
    }

这仅允许用户在单击九列之一时按“Achternaam”进行排序。我想要的是当用户点击 Nationaliteit 列时,数据会在顶部以 An 进行排序。以此类推每一列

这是列表播放器列表:

namespace SimulatorSimulator
{
    class SpelerData
    {
        public string Voornaam { get; set; }
        public string Achternaam { get; set; }
        public string Positie { get; set; }
        public string Nationaliteit { get; set; }
        public int Age { get; set; }
        public int Aanval { get; set; }
        public int Verdediging { get; set; }
        public int Gemiddeld { get; set; }
        public string TransferWaarde { get; set; }
    }
}

在主类中:

 List<SpelerData> listPlayers = new List<SpelerData>();

一些虚拟数据:

Romelu;Lukaku;Aanvaller;Belgie;22;87;12;50;41.000.000,00    
Raheem ;Sterling;Aanvaller;Engeland;21;84;30;57;35.000.000,00    
Zlatan ;Ibrahimovic;Aanvaller;Zweden;34;87;21;54;34.500.000,00

【问题讨论】:

  • @KevinTinnemans 请更仔细地考虑 Ivan Stoev 对 Ian 回答的评论。我可能会因为这样说而被激怒,这可能会被误解,因为我发布了自己的答案,但接受的答案无论如何都不会帮助您成为更好的开发人员。蛮力是一个起点,当您无法提出更好的解决方案时,您会退回到该解决方案 - 对于这个特定问题有几个解决方案。这将导致不良做法和难以管理的代码。

标签: c# datagridview


【解决方案1】:

我确实认为,对于您的情况,最简单的方法是将您的数据放在 Database 表中。这样,您可以简单地将其用作data sourcedataGridView2,然后单击标题列即可轻松进行排序。

另一种方法是使用SortableBindingList (article),正如其他答案所建议的那样。

但是如果这两个选项都不在您的选择之列,我能想到的下一个最简单的方法是从ColumnHeaderMouseClick 创建事件,然后您可以通过使用e.ColumnIndex 和正确的“映射”来相应地列出您的排序(字典)给你准备好的IEnumerable&lt;SpelerData&gt;

因此,在您的表单加载中,您执行以下操作:

Dictionary<int, IEnumerable<SpelerData>> queryDict = new Dictionary<int, IEnumerable<SpelerData>>(); //Prepare a dictionary of query
private void form_load(object sender, EventArgs e) {
    dataGridView2.DataSource = listPlayers.OrderBy(x => x.Achternaam).ToList();
    queryDict.Add(0, listPlayers.OrderBy(x => x.Voornaam));
    queryDict.Add(1, listPlayers.OrderBy(x => x.Achternaam));
    queryDict.Add(2, listPlayers.OrderBy(x => x.Positie));
    queryDict.Add(3, listPlayers.OrderBy(x => x.Nationaliteit));
    queryDict.Add(4, listPlayers.OrderBy(x => x.Age));
    queryDict.Add(5, listPlayers.OrderBy(x => x.Aanval));
    queryDict.Add(6, listPlayers.OrderBy(x => x.Verdediging));
    queryDict.Add(7, listPlayers.OrderBy(x => x.Gemiddeld));
    queryDict.Add(8, listPlayers.OrderBy(x => x.TransferWaarde));
}

然后在 ColumnHeaderMouseClick 事件中,只需执行以下操作:

private void dataGridView2_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) {
    dataGridView2.DataSource = queryDict[e.ColumnIndex].ToList();
}

你会得到你想要的行为。

请注意,由于IEnumerable 是延迟执行,因此Dictionary 的早期准备完全不会影响性能。您唯一需要添加的是 form_Load 中的 9 行代码来准备字典和 dataGridView2_ColumnHeaderMouseClick 事件中的 1 行代码。这是除了我之前提到的两个我能想到的最简单的解决方案。

【讨论】:

  • “这是除了我之前提到的两个我能想到的最简单的解决方案。” 虽然这适用于特定情况,但它更像是一种解决方法而不是解决方案.如果您在另一个网格中需要相同的内容 - 又需要 10 行代码怎么办?第三网格等?此外,重新分配数据源的效率非常低,并且有副作用。 IBindingListIBindingListView 接口专门用于处理这种情况和类似情况。创建一个基本的泛型类可能看起来需要更多的编码,但它是值得的,因为它是一次性的,可以在数千个地方重复使用。
【解决方案2】:

你可以使用 SortableBindingList

SortableBindingList<T> list = new SortableBindingList<T>();

//Add items to list

dataGridView.DataSource = list ;

这将允许在单击列标题时进行排序

public class SortableBindingList<T> : BindingList<T>
{
    private readonly Dictionary<Type, PropertyComparer<T>> comparers;
    private bool isSorted;
    private ListSortDirection listSortDirection;
    private PropertyDescriptor propertyDescriptor;

    public SortableBindingList()
        : base(new List<T>())
    {
        this.comparers = new Dictionary<Type, PropertyComparer<T>>();
    }

    public SortableBindingList(IEnumerable<T> enumeration)
        : base(new List<T>(enumeration))
    {
        this.comparers = new Dictionary<Type, PropertyComparer<T>>();
    }

    protected override bool SupportsSortingCore
    {
        get { return true; }
    }

    protected override bool IsSortedCore
    {
        get { return this.isSorted; }
    }

    protected override PropertyDescriptor SortPropertyCore
    {
        get { return this.propertyDescriptor; }
    }

    protected override ListSortDirection SortDirectionCore
    {
        get { return this.listSortDirection; }
    }

    protected override bool SupportsSearchingCore
    {
        get { return true; }
    }

    protected override void ApplySortCore(PropertyDescriptor property, ListSortDirection direction)
    {
        List<T> itemsList = (List<T>)this.Items;

        Type propertyType = property.PropertyType;
        PropertyComparer<T> comparer;
        if (!this.comparers.TryGetValue(propertyType, out comparer))
        {
            comparer = new PropertyComparer<T>(property, direction);
            this.comparers.Add(propertyType, comparer);
        }

        comparer.SetPropertyAndDirection(property, direction);
        itemsList.Sort(comparer);

        this.propertyDescriptor = property;
        this.listSortDirection = direction;
        this.isSorted = true;

        this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
    }

    protected override void RemoveSortCore()
    {
        this.isSorted = false;
        this.propertyDescriptor = base.SortPropertyCore;
        this.listSortDirection = base.SortDirectionCore;

        this.OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
    }

    protected override int FindCore(PropertyDescriptor property, object key)
    {
        int count = this.Count;
        for (int i = 0; i < count; ++i)
        {
            T element = this[i];
            if (property.GetValue(element).Equals(key))
            {
                return i;
            }
        }

        return -1;
    }
}

【讨论】:

    【解决方案3】:

    如果您愿意使用反射,可以执行以下操作。

    注意:我假设您使用的是DataGridView.ColumnHeaderMouseClick Event,但这不会改变这种方法的核心。关键是您需要动态识别表示您想要OrderBy 的列名称的字符串值。如果你真的需要/想要的话,你可以硬编码这个关联。

    private void dataGridView2_ColumnHeaderMouseClick(
        object sender, DataGridViewCellMouseEventArgs e)
    {
        ...
        var sortCol = dataGridView2.Columns[e.ColumnIndex];
        var colName = sortCol.Name;
    
        dataGridView2.DataSource = listPlayers.Select(s => new { voornaam = s.Voornaam, Achternaam = s.Achternaam, positie = s.Positie, Nationaltieit = s.Nationaliteit, Leeftijd = s.Age, Aanval = s.Aanval, Verdediging = s.Verdediging, Gemiddeld = s.Gemiddeld, waarde = s.TransferWaarde })
                                              .OrderBy(s => typeof(SpelerData).GetProperty(colName))
                                              .ToList();
        ...
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-11-18
      • 2014-10-21
      • 2012-07-23
      • 2013-07-06
      • 1970-01-01
      • 2012-04-06
      相关资源
      最近更新 更多