Як увімкнути сортування DataGridView, коли користувач натискає заголовок стовпця?


74

У моїй формі є datagridview, і я заповнюю її наступним чином:

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

Зараз я використовую s.Apellidos як сортування за замовчуванням, але я також хотів би дозволити користувачам сортувати, натискаючи на заголовок стовпця.

Таке сортування жодним чином не змінить дані, це лише бонус на стороні клієнта, який полегшить пошук інформації при скануванні екрана очима.

Дякую за пропозиції.


Відповіді:


51

Встановіть для властивості SortMode для всіх стовпців (які можуть сортувати користувачі) значення Автоматично

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

Змінити: оскільки ваш datagridview пов’язаний із запитом linq, він не буде відсортований. Тож, будь ласка, пройдіть це, [404 dead link, see next section]де пояснюється, як створити сортувальний список прив’язки, а потім подати його як джерело даних до datagridview.

Код, відновлений із мертвого посилання

Посилання зверху 404 мертві. Я відновив код з архіву сторінки Інтернету Wayback Machine .

public Form1()
{
    InitializeComponent();

    SortableBindingList<person> persons = new SortableBindingList<person>();
    persons.Add(new Person(1, "timvw", new DateTime(1980, 04, 30)));
    persons.Add(new Person(2, "John Doe", DateTime.Now));

    this.dataGridView1.AutoGenerateColumns = false;
    this.ColumnId.DataPropertyName = "Id";
    this.ColumnName.DataPropertyName = "Name";
    this.ColumnBirthday.DataPropertyName = "Birthday";
    this.dataGridView1.DataSource = persons;
}

Оскільки я хочу, щоб усі стовпці можна було сортувати, чи є спосіб програмно перебирати кожен стовпець і встановлювати цю властивість?

@Niraj: Я отримую помилку компілятора. Я спробував змінити вашу відповідь на column.Name, але, схоже, це ніяк не змінює сортування.

2
@Sergio: Отже, якщо я правильно розумію, тепер ви не отримуєте жодної помилки, але все-таки стовпці не можна сортувати.
Маршал

2
@Niraj: Правильно. Я натискаю на заголовок стовпця, і рядки не впорядковуються самі.

4
До речі, ви foreachможете бути простішими:foreach(DataGridViewColumn column in dataGridView1.Columns) column.SortMode = DataGridViewColumnSortMode.Automatic;
Тім Шмельтер

28

Як запропонував Нірадж, використовуйте SortableBindingList . Я дуже успішно використовував це з DataGridView.

Ось посилання на оновлений код, який я використовував - Представлення списку SortableBinding - Take Take - архів

Просто додайте два вихідні файли до свого проекту, і ви будете в бізнесі.

Джерело знаходиться в SortableBindingList.zip -404 dead link


Це рішення не потребує LINQ і працює в .net 2.0, а в BindingList є інші корисні функції, такі як повідомлення про зміни. Дякую, що поділились.
Мехран

1
Джерело вже не існує, чи можна це оновити?
Аренд

Саме те, що я шукав. Дякую.
Matt Martin

1
@Arend Я додав посилання на архів зворотних машин мертвого посилання
Вальтер Стабош,

10

Ще один спосіб зробити це - використання бібліотеки "System.Linq.Dynamic". Ви можете отримати цю бібліотеку у Nuget . Не потрібно ніяких спеціальних реалізацій або списку, що сортується :)

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

1
Працює як шарм, не можу повірити, що ще ніхто не дав вам позитивних відгуків про це
Scope Creep

Використання OrderByDescendingдля elseсправи було б кращим.
Безнадійний

чудово працює! Не забудьте додати dataGridView.ColumnHeaderMouseClick += dataGridView_ColumnHeaderMouseClick;І реалізувати це для BindingList, який оновлюється кожного разу, коли надходять дані ... Ви повинні змінити код ...dataGridView.DataSource = new BindingList<Data>(dataList.OrderBy(dataGridView.Columns[e.ColumnIndex].DataPropertyName).ToList());
hyukkyulee

6

Ваша сітка даних повинна бути прив’язана до списку, який можна сортувати.

Створіть цей обробник подій:

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

І ініціалізуйте подію кожного з ваших datragrids таким чином:

        dataGridView1.DataBindingComplete += MakeColumnsSortable_DataBindingComplete;

Мені сподобалась ідея використання події DataBindingComplete, але це не стосується відсутності сортуваного набору даних. Я відредагував його, щоб зробити його трохи більш багаторазовим
Стівен Тернер

6

Вам не потрібно створювати прив'язувальний джерело даних. Якщо ви хочете застосувати сортування для всіх своїх стовпців, ось більш загальне рішення для мене;

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

Не забудьте підписати свою сітку даних на подію ColumnHeaderMouseClick. Коли користувач натискає стовпець, він сортуватиме за спаданням. Якщо ще раз клацнути той самий заголовок стовпця, сортування буде застосовано за зростанням.


Гарне рішення. Дякую.
timanderson

5

Ви можете використовувати подію DataGridViewColoumnHeaderMouseClick наступним чином:

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

я хочу використати цю ідею, але як я можу керувати, який заголовок стовпця клацнув? щоб сортувати порядок datagridview за цією конкретною назвою стовпця? подяка
Махді Рашиді

@ mr.dev.eloper - Об'єкт DataGridViewCellMouseEventArgs містить властивість "ColumnIndex".
jjspierx,

3


є досить просте рішення при використанні Entity Framework (у цьому випадку версія 6). Я не впевнений, але, схоже, ObservableCollectionExtensions.ToBindingList<T>метод повертає реалізацію сортувального списку прив'язок . Я не знайшов вихідний код для підтвердження цього припущення, але об'єкт, що повертається з цього методу, працюєDataGridView дуже добре, особливо при сортуванні стовпців, натискаючи на його заголовки.

Код дуже простий і покладається лише на .net та класи фреймворків:

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;

2

ПОЦІЛУЙ: Будь простим, дурним

Шлях A: Реалізуйте власний клас SortableBindingList, коли хочете використовувати DataBinding та сортування .

Шлях B: Використання List <string> сортування також працює, але не працює з DataBinding .


1

Якщо ви отримуєте повідомлення про помилку типу

Неопрацьований виняток типу 'System.NullReferenceException' стався в System.Windows.Forms.dll

якщо ви працюєте з SortableBindingList, ваш код, ймовірно, використовує певні цикли над рядками DataGridView, а також намагається отримати доступ до порожнього останнього рядка! (BindingSource = null)

Якщо вам не потрібно дозволяти користувачеві додавати нові рядки безпосередньо в DataGridView, цей рядок коду легко вирішить проблему:

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

1
  1. Створіть клас, який містить усі необхідні вам властивості, та заповніть їх у конструкторі

    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. Створіть клас IComparer <Student> для сортування

    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. У формі, що містить сітку даних, додайте

    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. В обробнику подій datagridview_ColumnHeaderMouseClick виконайте щось подібне

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

Сподіваюся, це допомагає


1

Я пропоную використовувати DataTable.DefaultView як DataSource. Потім рядок нижче.

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

Після цього сам gridview буде керувати сортуванням (підтримується зростаючий або спадання).


1

помістіть цей рядок у форму Windows (при завантаженні або краще у загальнодоступному методі, наприклад "binddata"):

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

Помістіть цей код у файл, який називається DataGridViewExtensions.cs (або подібний)

// 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

Про всяк випадок, якщо хтось все ще шукає його, я зробив це на VS 2008 C #.

На Event ColumnHeaderMouseClick додайте прив’язку даних для gridview та надішліть замовлення за полем як параметр. Ви можете отримати натиснуте поле наступним чином:

dgView.Columns[e.ColumnIndex].Name

У моєму випадку імена заголовків схожі на назви полів перегляду.


0

У мене є прив’язка об’єкта BindingList <> як джерела даних до dataGridView.

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

Коли я натиснув заголовок стовпця, сортування не відбувається. Я використав відповідь SortableBindingList, надану Томом Бушеллом. Включивши два вихідні файли до свого проекту

  1. SortableBindingList.cs
  2. PropertyComparer.cs

Тоді ця зміна вноситься в мій код:

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;

Після цих змін я виконав збірку своєї програми. Тепер я можу сортувати, натискаючи заголовки стовпців. Потрібно змінити лише два рядки, вони виділені у фрагменті коду вище, відстежуючи коментарі.


0

У моєму випадку проблема полягала в тому, що я встановив свій DataSourceяк object, і саме тому він не був відсортований. Після зміни відobject на це DataTableвін добре працював без будь-якого доповнення коду.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.