DataGridView
sorting
column header
C#
user interface

How to enable DataGridView sorting when user clicks on the column header?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The DataGridView control in Windows Forms supports column sorting when users click column headers. If the data source implements IBindingList with sorting support (like DataTable), sorting works automatically. For custom collections like List<T> or BindingList<T>, you need to either switch to a sortable data source, set the SortMode property on columns, or handle sorting manually via the ColumnHeaderMouseClick event. This article covers all three approaches.

Automatic Sorting with DataTable

csharp
1// DataTable supports sorting automatically
2var table = new DataTable();
3table.Columns.Add("Name", typeof(string));
4table.Columns.Add("Age", typeof(int));
5table.Columns.Add("City", typeof(string));
6
7table.Rows.Add("Alice", 30, "New York");
8table.Rows.Add("Bob", 25, "Chicago");
9table.Rows.Add("Charlie", 35, "Boston");
10
11dataGridView1.DataSource = table;
12// Clicking column headers sorts automatically — no extra code needed

DataTable implements IBindingListView, which includes full sorting support. The DataGridView detects this and enables header-click sorting with ascending/descending toggle.

Setting Column SortMode

csharp
1// Set SortMode to Automatic for each column
2foreach (DataGridViewColumn column in dataGridView1.Columns)
3{
4    column.SortMode = DataGridViewColumnSortMode.Automatic;
5}
6
7// Or set individually
8dataGridView1.Columns["Name"].SortMode = DataGridViewColumnSortMode.Automatic;
9dataGridView1.Columns["Age"].SortMode = DataGridViewColumnSortMode.Automatic;
10
11// Disable sorting on a specific column
12dataGridView1.Columns["Actions"].SortMode = DataGridViewColumnSortMode.NotSortable;

DataGridViewColumnSortMode.Automatic shows the sort glyph (arrow) on the header and handles click events. Programmatic mode shows the glyph but requires you to call Sort() manually. NotSortable disables sorting entirely for that column.

Manual Sorting with List of Objects

csharp
1public class Employee
2{
3    public string Name { get; set; }
4    public int Age { get; set; }
5    public decimal Salary { get; set; }
6}
7
8private List<Employee> _employees;
9private bool _sortAscending = true;
10
11private void Form1_Load(object sender, EventArgs e)
12{
13    _employees = new List<Employee>
14    {
15        new Employee { Name = "Alice", Age = 30, Salary = 75000 },
16        new Employee { Name = "Bob", Age = 25, Salary = 65000 },
17        new Employee { Name = "Charlie", Age = 35, Salary = 85000 },
18    };
19
20    dataGridView1.DataSource = new BindingList<Employee>(_employees);
21
22    // Set all columns to Programmatic sort mode
23    foreach (DataGridViewColumn col in dataGridView1.Columns)
24        col.SortMode = DataGridViewColumnSortMode.Programmatic;
25}
26
27private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
28{
29    var columnName = dataGridView1.Columns[e.ColumnIndex].DataPropertyName;
30
31    if (_sortAscending)
32        _employees = _employees.OrderBy(x => x.GetType().GetProperty(columnName).GetValue(x)).ToList();
33    else
34        _employees = _employees.OrderByDescending(x => x.GetType().GetProperty(columnName).GetValue(x)).ToList();
35
36    _sortAscending = !_sortAscending;
37    dataGridView1.DataSource = new BindingList<Employee>(_employees);
38}

When using List<T>, sorting must be handled manually because List<T> does not implement IBindingList. The ColumnHeaderMouseClick event provides the column index to determine which property to sort by.

Using SortableBindingList

csharp
1// A reusable sortable binding list
2public class SortableBindingList<T> : BindingList<T>
3{
4    private bool _isSorted;
5    private PropertyDescriptor _sortProperty;
6    private ListSortDirection _sortDirection;
7
8    protected override bool SupportsSortingCore => true;
9    protected override bool IsSortedCore => _isSorted;
10    protected override PropertyDescriptor SortPropertyCore => _sortProperty;
11    protected override ListSortDirection SortDirectionCore => _sortDirection;
12
13    protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
14    {
15        var items = Items as List<T>;
16        if (items == null) return;
17
18        items.Sort((x, y) =>
19        {
20            var xValue = prop.GetValue(x);
21            var yValue = prop.GetValue(y);
22            int result = Comparer<object>.Default.Compare(xValue, yValue);
23            return direction == ListSortDirection.Descending ? -result : result;
24        });
25
26        _isSorted = true;
27        _sortProperty = prop;
28        _sortDirection = direction;
29        OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
30    }
31
32    protected override void RemoveSortCore()
33    {
34        _isSorted = false;
35    }
36}
37
38// Usage — automatic sorting now works with custom objects
39var list = new SortableBindingList<Employee>();
40list.Add(new Employee { Name = "Alice", Age = 30, Salary = 75000 });
41list.Add(new Employee { Name = "Bob", Age = 25, Salary = 65000 });
42
43dataGridView1.DataSource = list;
44// Column headers are now clickable for sorting

SortableBindingList<T> overrides the sorting methods of BindingList<T>, making it compatible with DataGridView's built-in sorting mechanism.

Programmatic Sort

csharp
1// Sort programmatically without user interaction
2dataGridView1.Sort(dataGridView1.Columns["Age"], ListSortDirection.Ascending);
3
4// Sort with a custom comparer
5dataGridView1.Sort(new EmployeeComparer());
6
7public class EmployeeComparer : IComparer
8{
9    public int Compare(object x, object y)
10    {
11        var row1 = (DataGridViewRow)x;
12        var row2 = (DataGridViewRow)y;
13        return string.Compare(
14            row1.Cells["Name"].Value?.ToString(),
15            row2.Cells["Name"].Value?.ToString(),
16            StringComparison.OrdinalIgnoreCase);
17    }
18}

Common Pitfalls

  • Using List<T> as DataSource: List<T> does not support IBindingList sorting. Clicking column headers does nothing. Use DataTable, SortableBindingList<T>, or handle ColumnHeaderMouseClick manually.
  • SortMode set to NotSortable: The default SortMode for auto-generated columns depends on the data source. If sorting does not work, check that the column's SortMode is Automatic or Programmatic, not NotSortable.
  • Reassigning DataSource clears selection: Setting DataSource to a new list after sorting resets scroll position and selection. Use SortableBindingList<T> to sort in-place without reassigning.
  • Virtual mode conflicts: When VirtualMode is true, the DataGridView does not manage data directly. Sorting must be handled entirely in the CellValueNeeded event and your backing data store.
  • Sort glyph not showing: The sort arrow only appears when SortMode is Automatic or Programmatic. With manual ColumnHeaderMouseClick handling, set SortGlyphDirection on the column explicitly after sorting.

Summary

  • DataTable and DataView as data sources enable automatic column sorting with no extra code
  • Set column.SortMode = DataGridViewColumnSortMode.Automatic for built-in sort behavior
  • For List<T>, implement a SortableBindingList<T> or handle ColumnHeaderMouseClick manually
  • Use dataGridView1.Sort(column, direction) for programmatic sorting
  • Always check that SortMode is not NotSortable when sorting does not respond to header clicks

Course illustration
Course illustration

All Rights Reserved.