Wie aktiviere ich die DataGridView-Sortierung, wenn der Benutzer auf die Spaltenüberschrift klickt?

74

Ich habe eine Datagrid-Ansicht in meinem Formular und fülle sie folgendermaßen aus:

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

Jetzt verwende ich die s.Apellidos als Standardsortierung, möchte aber auch Benutzern das Sortieren erlauben, wenn sie auf die Spaltenüberschrift klicken.

Diese Art wird nicht die Daten in irgendeiner Art und Weise ändern, es ist nur ein Client - Seite Bonus für eine einfachere Suche nach Informationen zu ermöglichen , wenn der Bildschirm mit ihren Augen scannen.

Danke für die Vorschläge.


quelle

Antworten:

51

Setzen Sie alle SortMode-Eigenschaften aller Spalten (die von Benutzern sortiert werden können) auf Automatisch

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

Bearbeiten: Da Ihre Datagrid-Ansicht an eine Linq-Abfrage gebunden ist, wird sie nicht sortiert. Gehen Sie also bitte über diesen Link, der erklärt, wie Sie eine sortierbare Bindungsliste erstellen und diese dann als Datenquelle an datagridview weiterleiten.

Marschall
quelle
Gibt es eine Möglichkeit, jede Spalte programmgesteuert zu durchlaufen und diese Eigenschaft festzulegen, da alle Spalten sortierbar sein sollen?
@Niraj: Ich erhalte einen Compilerfehler. Ich habe versucht, Ihre Antwort in Spalte zu ändern. Name, aber es scheint die Sortierung in keiner Weise zu ändern.
2
@Sergio: Wenn ich das richtig verstehe, wird jetzt kein Fehler angezeigt, aber die Spalten sind immer noch nicht sortierbar.
Marschall
2
@Niraj: Richtig. Ich klicke auf die Spaltenüberschrift und die Zeilen ordnen sich nicht selbst.
4
Übrigens, Sie foreachkönnten einfacher sein:foreach(DataGridViewColumn column in dataGridView1.Columns) column.SortMode = DataGridViewColumnSortMode.Automatic;
Tim Schmelter
28

Verwenden Sie, wie Niraj vorgeschlagen hat, eine SortableBindingList . Ich habe dies sehr erfolgreich mit dem DataGridView verwendet.

Hier ist ein Link zu dem aktualisierten Code, den ich verwendet habe - Präsentieren der SortableBindingList - Take Two

Fügen Sie einfach die beiden Quelldateien zu Ihrem Projekt hinzu, und Sie sind im Geschäft.

Die Quelle befindet sich in SortableBindingList.zip

Tom Bushell
quelle
Diese Lösung benötigt kein LINQ und funktioniert in .net 2.0. In BindingList finden Sie weitere nützliche Funktionen wie die Änderungsbenachrichtigung. Danke für das Teilen.
Mehran
Die Quelle existiert nicht mehr, gibt es überhaupt eine Möglichkeit, dies zu aktualisieren?
Arend
Genau das, wonach ich gesucht habe. Vielen Dank.
Matt Martin
10

Eine weitere Möglichkeit hierfür ist die Verwendung der Bibliothek "System.Linq.Dynamic". Sie können diese Bibliothek von Nuget erhalten . Keine benutzerdefinierten Implementierungen oder sortierbaren Listen erforderlich :)

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;
}
Vidhyardhi Gorrepati
quelle
1
Funktioniert wie ein Zauber, kann nicht glauben, dass Ihnen noch niemand positives Feedback dazu gegeben hat
Scope Creep
Verwendung OrderByDescendingfür den elseFall wäre besser.
Hoffnungslos
funktioniert super! Vergessen Sie nicht hinzuzufügen dataGridView.ColumnHeaderMouseClick += dataGridView_ColumnHeaderMouseClick;und dies für BindingList zu implementieren, das jedes Mal aktualisiert wird, wenn Daten eingehen ... Sie müssen den Code ändern ...dataGridView.DataSource = new BindingList<Data>(dataList.OrderBy(dataGridView.Columns[e.ColumnIndex].DataPropertyName).ToList());
hyukkyulee
6

Ihr Datenraster muss zunächst an eine sortierbare Liste gebunden sein.

Erstellen Sie diesen Ereignishandler:

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

Und initialisieren Sie das Ereignis jedes Ihrer Datragrids wie folgt:

        dataGridView1.DataBindingComplete += MakeColumnsSortable_DataBindingComplete;
Saneesh B.
quelle
Ich mochte die Idee, das DataBindingComplete-Ereignis zu verwenden, aber dies betrifft nicht das Fehlen eines sortierbaren Datasets. Ich habe es bearbeitet, um es ein bisschen wiederverwendbarer zu machen
Stephen Turner
6

Sie müssen keine verbindliche Datenquelle erstellen. Wenn Sie die Sortierung für alle Ihre Spalten anwenden möchten, finden Sie hier eine allgemeinere Lösung von mir.

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

Stellen Sie sicher, dass Sie Ihr Datenraster für das Ereignis abonnieren ColumnHeaderMouseClick. Wenn der Benutzer auf die Spalte klickt, wird sie nach absteigend sortiert. Wenn dieselbe Spaltenüberschrift erneut angeklickt wird, wird die Sortierung durch Aufsteigen angewendet.

0014
quelle
Schöne Lösung. Vielen Dank.
Timanderson
5

Sie können das DataGridViewColoumnHeaderMouseClick-Ereignis folgendermaßen verwenden:

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()
    }
}
Thinzar
quelle
Ich möchte diese Idee verwenden, aber wie kann ich verwalten, auf welche Spaltenüberschrift geklickt wurde? Datagridview-Reihenfolge nach diesem bestimmten Spaltennamen sortieren? danke
Mahdi Rashidi
@ mr.dev.eloper - Das DataGridViewCellMouseEventArgs-Objekt enthält die Eigenschaft "ColumnIndex".
jjspierx
3


Bei Verwendung von Entity Framework (in diesem Fall Version 6) gibt es eine ganz einfache Lösung. Ich bin nicht sicher, aber es scheint, dass die ObservableCollectionExtensions.ToBindingList<T>Methode die Implementierung einer sortierbaren Bindungsliste zurückgibt . Ich habe keinen Quellcode gefunden, um diese Annahme zu bestätigen, aber das von dieser Methode zurückgegebene Objekt funktioniert DataGridViewsehr gut, insbesondere beim Sortieren von Spalten durch Klicken auf die Überschriften.

Der Code ist sehr einfach und basiert nur auf .net- und Entity-Framework-Klassen:

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;
Adam Matecki
quelle
2

KISS: Halte es einfach, dumm

Weg A: Implementieren Sie eine eigene SortableBindingList- Klasse, wenn Sie DataBinding und Sorting verwenden möchten .

Weg B: Verwenden Sie eine List <String> -Sortierung funktioniert auch, aber nicht mit DataBinding .

hfrmobile
quelle
1

Wenn Sie eine Fehlermeldung wie erhalten

In System.Windows.Forms.dll ist eine nicht behandelte Ausnahme vom Typ 'System.NullReferenceException' aufgetreten

Wenn Sie mit SortableBindingList arbeiten, verwendet Ihr Code wahrscheinlich einige Schleifen über DataGridView-Zeilen und versucht auch, auf die leere letzte Zeile zuzugreifen! (BindingSource = null)

Wenn Sie dem Benutzer nicht erlauben müssen, neue Zeilen direkt in DataGridView hinzuzufügen, kann das Problem mit dieser Codezeile leicht behoben werden:

InitializeComponent();
m_dataGridView.AllowUserToAddRows = false; // after components initialized
...
leon22
quelle
1
  1. Erstellen Sie eine Klasse, die alle benötigten Eigenschaften enthält, und füllen Sie sie im Konstruktor aus

    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. Erstellen Sie eine IComparer <Student> -Klasse, um sortieren zu können

    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. Innerhalb des Formulars mit dem Datagrid hinzufügen

    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. Führen Sie in datagridview_ColumnHeaderMouseClick den Ereignishandler so etwas aus

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

Hoffe das hilft

Albert
quelle
1

Ich schlage vor, eine DataTable.DefaultView als DataSource zu verwenden. Dann die Zeile unten.

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

Danach verwaltet die Rasteransicht selbst die Sortierung (Aufsteigend oder Absteigend wird unterstützt.)

Milad
quelle
1

Fügen Sie diese Zeile in Ihr Windows-Formular ein (beim Laden oder besser in einer öffentlichen Methode wie "binddata"):

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

Fügen Sie diesen Code in eine Datei mit dem Namen DataGridViewExtensions.cs (oder ähnlich) ein.

// 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);
    }
}
Mark Lloyd
quelle
0

Nur für den Fall, dass noch jemand danach sucht, habe ich es auf VS 2008 C # gemacht.

Fügen Sie im Ereignis ColumnHeaderMouseClick eine Datenbindung für die Rasteransicht hinzu und senden Sie die Reihenfolge nach Feld wie ein Parameter. Sie können das angeklickte Feld wie folgt erhalten:

dgView.Columns[e.ColumnIndex].Name

In meinem Fall ähneln die Namen der Header den Namen der Anzeigefelder.

Hugo Bazan
quelle
0

Ich habe ein BindingList <> -Objekt als Datenquelle an dataGridView gebunden.

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

Wenn ich auf die Spaltenüberschrift geklickt habe, findet keine Sortierung statt. Ich habe die SortableBindingList-Antwort von Tom Bushell verwendet. Nachdem ich zwei Quelldateien in mein Projekt aufgenommen habe

  1. SortableBindingList.cs
  2. PropertyComparer.cs

Dann wird diese Änderung an meinem Code vorgenommen:

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;

Nach diesen Änderungen habe ich ein Build für mein Programm durchgeführt. Ich kann jetzt sortieren, indem ich auf die Spaltenüberschriften klicke. Es müssen nur zwei Zeilen geändert werden. Sie werden im obigen Codeausschnitt durch nachfolgende Kommentare hervorgehoben.

user3674642
quelle
0

In meinem Fall war das Problem, dass ich meine DataSourceals festgelegt hatte object, weshalb sie nicht sortiert wurde. Nach dem Wechsel von objectzu a DataTablefunktionierte es gut ohne Code-Ergänzung.

Elvis Silva Noleto
quelle