web-dev-qa-db-fra.com

Comment activer le tri DataGridView lorsque l'utilisateur clique sur l'en-tête de colonne?

J'ai un datagridview sur mon formulaire et je le remplis avec ceci:

dataGridView1.DataSource = students.Select(s => new { ID = s.StudentId, RUDE = s.RUDE, Nombre = s.Name, Apellidos = s.LastNameFather + " " + s.LastNameMother, Nacido = s.DateOfBirth })
                                   .OrderBy(s => s.Apellidos)
                                   .ToList();

Maintenant, j'utilise le s.Apellidos comme le tri par défaut, mais je voudrais aussi permettre aux utilisateurs de trier en cliquant sur l'en-tête de la colonne.

Cette sorte de pas modifiera les données de quelque manière que ce soit, il s’agira simplement d’un bonus côté client pour permettre une recherche plus facile des informations lors du balayage de l’écran avec les yeux.

Merci pour les suggestions.

66
delete

Définissez la propriété SortMode de la colonne (pouvant être triée par les utilisateurs) sur Automatique. 

dataGridView1.DataSource = students.Select(s => new { ID = s.StudentId, RUDE = s.RUDE, Nombre = s.Name, Apellidos = s.LastNameFather + " " + s.LastNameMother, Nacido = s.DateOfBirth })
                                   .OrderBy(s => s.Apellidos)
                                   .ToList();

    foreach(DataGridViewColumn column in dataGridView1.Columns)
    {

        column.SortMode = DataGridViewColumnSortMode.Automatic;
    }

Edit: Comme votre datagridview est lié à une requête linq, il ne sera pas trié. Veuillez donc passer par ce link qui explique comment créer une liste de liaisons triable, puis l’alimenter en tant que source de données vers datagridview.

46
Marshal

Comme suggéré par Niraj, utilisez un SortableBindingList. J'ai utilisé cela avec beaucoup de succès avec le DataGridView.

Voici un lien vers le code mis à jour que j'ai utilisé - Présentation de SortableBindingList - Take Two

Ajoutez simplement les deux fichiers source à votre projet et vous serez en affaires.

La source est dans SortableBindingList.Zip

26
Tom Bushell

votre grille de données doit être liée à une liste triable en premier lieu.

Créez ce gestionnaire d'événements:

    void MakeColumnsSortable_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
    {
        //Add this as an event on DataBindingComplete
        DataGridView dataGridView = sender as DataGridView;
        if (dataGridView == null)
        {
            var ex = new InvalidOperationException("This event is for a DataGridView type senders only.");
            ex.Data.Add("Sender type", sender.GetType().Name);
            throw ex;
        }

        foreach (DataGridViewColumn column in dataGridView.Columns)
            column.SortMode = DataGridViewColumnSortMode.Automatic;
    }

Et initialisez l'événement de chacun de vos datragrides comme ceci:

        dataGridView1.DataBindingComplete += MakeColumnsSortable_DataBindingComplete;
7
Saneesh B

Vous pouvez utiliser l'événement DataGridViewColoumnHeaderMouseClick comme suit:

Private string order = String.Empty;
private void dgvDepartment_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
    if (order == "d")
{
        order = "a";                
dataGridView1.DataSource = students.Select(s => new { ID = s.StudentId, RUDE = s.RUDE, Nombre = s.Name, Apellidos = s.LastNameFather + " " + s.LastNameMother, Nacido = s.DateOfBirth })   .OrderBy(s => s.Apellidos).ToList();
    }
    else
    {
        order = "d";
        dataGridView1.DataSource = students.Select(s => new { ID = s.StudentId, RUDE = s.RUDE, Nombre = s.Name, Apellidos = s.LastNameFather + " " + s.LastNameMother, Nacido = s.DateOfBirth }.OrderByDescending(s => s.Apellidos)  .ToList()
    }
}
5
thinzar

Vous n'avez pas besoin de créer une source de données de liaison. Si vous souhaitez appliquer le tri à toutes vos colonnes, voici une solution plus générique.

private int _previousIndex;
private bool _sortDirection;

private void gridView_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
    if (e.ColumnIndex == _previousIndex)
        _sortDirection ^= true; // toggle direction

    gridView.DataSource = SortData(
        (List<MainGridViewModel>)gridReview.DataSource, gridReview.Columns[e.ColumnIndex].Name, _sortDirection);

    _previousIndex = e.ColumnIndex;
}

public List<MainGridViewModel> SortData(List<MainGridViewModel> list, string column, bool ascending)
{
    return ascending ? 
        list.OrderBy(_ => _.GetType().GetProperty(column).GetValue(_)).ToList() :
        list.OrderByDescending(_ => _.GetType().GetProperty(column).GetValue(_)).ToList();
}

Assurez-vous de souscrire votre grille de données à l'événement ColumnHeaderMouseClick. Lorsque l'utilisateur clique sur la colonne, celle-ci sera triée par ordre décroissant. Si vous cliquez à nouveau sur le même en-tête de colonne, le tri sera appliqué par ordre croissant.

4
0014


il existe tout simplement une solution lorsque vous utilisez Entity Framework (version 6 dans ce cas). Je ne suis pas sûr mais il semble que la méthode ObservableCollectionExtensions.ToBindingList<T> retourne l'implémentation de sortable binding. Je n'ai pas trouvé de code source pour confirmer cette supposition, mais les objets renvoyés par cette méthode fonctionnent très bien avec DataGridView, en particulier lors du tri des colonnes en cliquant sur leurs en-têtes.

Le code est très simple et ne repose que sur les classes de framework .net et entity:

using System.Data.Entity;

IEnumerable<Item> items = MethodCreatingItems();

var observableItems = new System.Collections.ObjectModel.ObservableCollection<Item>(items);
System.ComponentModel.BindingList<Item> source = observableItems.ToBindingList();

MyDataGridView.DataSource = source;
3
Adam Matecki

Une autre méthode consiste à utiliser la bibliothèque "System.Linq.Dynamic". Vous pouvez vous procurer cette bibliothèque auprès de Nuget. Pas besoin d'implémentations personnalisées ou de liste triable :)

using System.Linq.Dynamic;
private bool sortAscending = false;

private void dataGridView_ColumnHeaderMouseClick ( object sender, DataGridViewCellMouseEventArgs e )
{
    if ( sortAscending )
        dataGridView.DataSource = list.OrderBy ( dataGridView.Columns [ e.ColumnIndex ].DataPropertyName ).ToList ( );
    else
        dataGridView.DataSource = list.OrderBy ( dataGridView.Columns [ e.ColumnIndex ].DataPropertyName ).Reverse ( ).ToList ( );
    sortAscending = !sortAscending;
}
3

KISS: Keep it simple, stupide

Manière A: Implémentez une propre classe SortableBindingList lorsque vous souhaitez utiliser DataBinding et sorting.

Manière B: Utiliser un tri List <string> fonctionne également mais ne fonctionne pas avec DataBinding.

2
hfrmobile

Si vous recevez un message d'erreur comme

Une exception non gérée du type 'System.NullReferenceException' s'est produite dans System.Windows.Forms.dll

si vous travaillez avec SortableBindingList, votre code utilise probablement des boucles sur les lignes DataGridView et tente également d'accéder à la dernière ligne vide! (BindingSource = null)

Si vous n'avez pas besoin d'autoriser l'utilisateur à ajouter de nouvelles lignes directement dans le DataGridView, cette ligne de code résout facilement le problème:

InitializeComponent();
m_dataGridView.AllowUserToAddRows = false; // after components initialized
...
1
leon22

Je suggère d'utiliser un DataTable.DefaultView en tant que DataSource. Puis la ligne ci-dessous.

foreach (DataGridViewColumn column in gridview.Columns)
    {
       column.SortMode = DataGridViewColumnSortMode.Automatic;
    }

Après cela, gridview gèrera lui-même le tri.

1
Milad
  1. Créez une classe contenant toutes les propriétés dont vous avez besoin et remplissez-les dans le constructeur.

    class Student
    {
        int _StudentId;
        public int StudentId {get;}
        string _Name;
        public string Name {get;}
        ...
    
        public Student(int studentId, string name ...)
        { _StudentId = studentId; _Name = name; ... }
    }
    
  2. Créer une classe IComparer <Etudiant>, pour pouvoir trier

    class StudentSorter : IComparer<Student>
    {
        public enum SField {StudentId, Name ... }
        SField _sField; SortOrder _sortOrder;
    
        public StudentSorder(SField field, SortOrder order)
        { _sField = field; _sortOrder = order;}
    
        public int Compare(Student x, Student y)
        {
            if (_SortOrder == SortOrder.Descending)
            {
                Student tmp = x;
                x = y;
                y = tmp;
            }
    
            if (x == null || y == null)
                return 0;
    
            int result = 0;
            switch (_sField)
            {
                case SField.StudentId:
                    result = x.StudentId.CompareTo(y.StudentId);
                    break;
                case SField.Name:
                    result = x.Name.CompareTo(y.Name);
                    break;
                    ...
            }
    
            return result;
        }
    }
    
  3. Dans le formulaire contenant la grille de données, ajoutez

    ListDictionary sortOrderLD = new ListDictionary(); //if less than 10 columns
    private SortOrder SetOrderDirection(string column)
    {
        if (sortOrderLD.Contains(column))
        {
            sortOrderLD[column] = (SortOrder)sortOrderLD[column] == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
        }
        else
        {
            sortOrderLD.Add(column, SortOrder.Ascending);
        }
    
        return (SortOrder)sortOrderLD[column];
    }
    
  4. Dans le gestionnaire d'événements datagridview_ColumnHeaderMouseClick, effectuez une opération similaire.

    private void dgv_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
    {
        StudentSorter sorter = null;
        string column = dGV.Columns[e.ColumnIndex].DataPropertyName; //Use column name if you set it
        if (column == "StudentId")
        {
            sorter = new StudentSorter(StudentSorter.SField.StudentId, SetOrderDirection(column));
        }
        else if (column == "Name")
        {
            sorter = new StudentSorter(StudentSorter.SField.Name, SetOrderDirection(column));
        }
    
        ...
    
        List<Student> lstFD = datagridview.DataSource as List<Student>;
        lstFD.Sort(sorter);
        datagridview.DataSource = lstFD;
        datagridview.Refresh();
    }
    

J'espère que cela t'aides

1
Albert

J'ai un objet BindingList <> se lier en tant que source de données à dataGridView.

BindingList x1;
x1 = new BindingList<sourceObject>();
BindingSource bsx1 = new BindingSource();
bsx1.DataSource = x1;
dataGridView1.DataSource = bsx1;

Lorsque j'ai cliqué sur l'en-tête de colonne, aucun tri n'a lieu. J'ai utilisé la réponse SortableBindingList fournie par Tom Bushell. Après avoir inclus deux fichiers sources dans mon projet

  1. SortableBindingList.cs
  2. PropertyComparer.cs

Ensuite, cette modification est apportée à mon code:

Be.Timvw.Framework.ComponentModel.SortableBindingList x1;                       // 1
x1 = new Be.Timvw.Framework.ComponentModel.SortableBindingList<sourceObject>(); // 2
BindingSource bsx1 = new BindingSource();
bsx1.DataSource = x1;
dataGridView1.DataSource = bsx1;

Après ces modifications, j’ai construit mon programme. Je peux maintenant trier en cliquant sur les en-têtes de colonne. Seules deux lignes doivent être modifiées, elles sont mises en évidence dans l'extrait de code ci-dessus par les commentaires de fin. 

0
user3674642

Dans mon cas, le problème était que j'avais défini ma DataSource comme object, c'est pourquoi elle n'a pas été triée. Après être passé de object à DataTable, il a bien fonctionné sans complément de code. 

0
Elvis Silva Noleto

Juste au cas où quelqu'un le chercherait encore, je l'ai fait sur VS 2008 C #.

Sur l'événement ColumnHeaderMouseClick, ajoutez une liaison de données pour gridview et envoyez le champ order by comme un paramètre. Vous pouvez obtenir le champ cliqué comme suit:

dgView.Columns[e.ColumnIndex].Name

Dans mon cas, les noms de l'en-tête sont similaires à ceux des champs de vue.

0
Hugo Bazan

mettez cette ligne dans votre formulaire Windows (au chargement ou mieux dans une méthode publique comme "binddata"): 

//
// bind the data and make the grid sortable 
//
this.datagridview1.MakeSortable( myenumerablecollection ); 

Placez ce code dans un fichier appelé DataGridViewExtensions.cs (ou similaire) 

// MakeSortable extension. 
// this will make any enumerable collection sortable on a datagrid view.  

//
// BEGIN MAKESORTABLE - Mark A. Lloyd
//
// Enables sort on all cols of a DatagridView 

//



    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Windows.Forms;

    public static class DataGridViewExtensions
    {
    public static void MakeSortable<T>(
        this DataGridView dataGridView, 
        IEnumerable<T> dataSource,
        SortOrder defaultSort = SortOrder.Ascending, 
        SortOrder initialSort = SortOrder.None)
    {
        var sortProviderDictionary = new Dictionary<int, Func<SortOrder, IEnumerable<T>>>();
        var previousSortOrderDictionary = new Dictionary<int, SortOrder>();
        var itemType = typeof(T);
        dataGridView.DataSource = dataSource;
        foreach (DataGridViewColumn c in dataGridView.Columns)
        {
            object Provider(T info) => itemType.GetProperty(c.Name)?.GetValue(info);
            sortProviderDictionary[c.Index] = so => so != defaultSort ? 
                dataSource.OrderByDescending<T, object>(Provider) : 
                dataSource.OrderBy<T,object>(Provider);
            previousSortOrderDictionary[c.Index] = initialSort;
        }

        async Task DoSort(int index)
        {

            switch (previousSortOrderDictionary[index])
            {
                case SortOrder.Ascending:
                    previousSortOrderDictionary[index] = SortOrder.Descending;
                    break;
                case SortOrder.None:
                case SortOrder.Descending:
                    previousSortOrderDictionary[index] = SortOrder.Ascending;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            IEnumerable<T> sorted = null;
            dataGridView.Cursor = Cursors.WaitCursor;
            dataGridView.Enabled = false;
            await Task.Run(() => sorted = sortProviderDictionary[index](previousSortOrderDictionary[index]).ToList());
            dataGridView.DataSource = sorted;
            dataGridView.Enabled = true;
            dataGridView.Cursor = Cursors.Default;

        }

        dataGridView.ColumnHeaderMouseClick+= (object sender, DataGridViewCellMouseEventArgs e) => DoSort(index: e.ColumnIndex);
    }
}
0
Mark Lloyd