Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make all datagridview columns sortable

I have a Windows form with a datagridview.

The ideal situation:

User clicks on any of the nine columns, and the program sorts all the data, if the clicked column contains numbers, I would like the lowest number on the top. If the clicked column contains a string I would like it to be sorted Alphabetically (A-Z).

What I have right now:

I saw an old question on Stack Overflow where the OP how to sort the datagridview when clicking "a" header. The difference with mine is that I want my datagridview to be sortable by any of the nine columns.

I have this code, stolen from the question I found:

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

This only lets the user order by "Achternaam" when he clicks one of the nine columns. What I want is when the user clicks on the Nationaliteit column, the data gets sorted with the An on top. And so on for every column

This is the listplayers list:

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

And in the main class:

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

Some dummy data:

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
like image 213
Kevin Tinnemans Avatar asked Jan 22 '16 10:01

Kevin Tinnemans


2 Answers

I do think that the simplest way for your case would be to put up your data in a Database table. This way, you could simply use this as the data source for your dataGridView2, and you will be able to do your sorting easily by clicking the header column.

An alternative way will be to use SortableBindingList (article) as the other answer has also suggested.

But if both options are not among your choices, the next simplest way I could think of is by creating event from ColumnHeaderMouseClick, and then you could list your sorting accordingly by making use of e.ColumnIndex and right "mapping" (Dictionary) to your prepared IEnumerable<SpelerData>

So, in your form load, you do something like this:

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

And then in the ColumnHeaderMouseClick event, simply do:

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

And you will get the behavior that you want.

Note that since IEnumerable is delayed execution, the early preparation of the Dictionary will not have impact on the performance at all. The only thing you need to add are the 9 lines of code in the form_Load to prepare the dictionary and 1 line of code in your dataGridView2_ColumnHeaderMouseClick event. This is the simplest solution apart from the two I mentioned earlier which I could think of.

like image 57
Ian Avatar answered Oct 17 '22 17:10

Ian


You could use a SortableBindingList

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

//Add items to list

dataGridView.DataSource = list ;

This will allow sorting when clicking on the column header

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;
    }
}
like image 26
darkend Avatar answered Oct 17 '22 16:10

darkend