Update: Sample Application
This is an old post I've been meaning to post since May, so here it is!
This one is a little strange, yet completely understandable.
I have an existing DataGridView with various bound items in it, a few of these items are lookup values - the grid uses a DataGridViewComboBoxColumn to cross-bind an ID with a list of ID/Lookup values.
The fun starts (or ends depending on your outlook in life) when the client asks why can't I sort on this column? Or, why doesn't this column sort correctly?
This is because in a bound DataGridView, the sorting action is delegated to the ApplySort of the underlying IBindingList. You can confirm this in Reflector if you disassemble the DataGridView and look through the SortInternal method. Simply, the grid code says "if you are data-bound, do this, else do that": 'this' is ApplySort, 'that' is Rows.Sort(IComparer, ListSortDirection).
One obvious solution is to create a dummy column and marshal the values between that and the underlying data column, so that when the users sort the unbound column - it sorts as they would expect; alphabetically.
My approach was slightly more contorted. In the derived DataGridView class we had (DataGridViewPlus), I created a new Sort method, taking an IComparer:
public virtual void Sort(IComparer<DataGridViewRow> comparer)
// We sort the grid rows using the provided comparer.
List<DataGridViewRow> sortedRows = new List<DataGridViewRow>();
foreach (DataGridViewRow row in this.Rows)
// We need to work with the DataView object.
BindingSource source = this.DataSource as BindingSource;
DataView view = (source.Current as DataRowView).DataView;
// We get the table structure - not any rows.
DataTable tempTable = view.Table.Clone();
// Importantly, we import the rows in the same order as our sorted list.
foreach (DataGridViewRow row in sortedRows)
tempTable.ImportRow((row.DataBoundItem as DataRowView).Row);
// We can't simply copy the table across, so we clear and re-import.
foreach (DataRow row in tempTable.Rows)
I then created a nested class for a comparer specific to sorting rows based on the FormattedValue of a cell:
public partial class DataGridViewPlus
public class RowDisplayTextComparer : IComparer, IComparer<DataGridViewRow>
#region Attributes & Properties
private static int SortModifier = 1;
private int _columnIndex = -1;
public RowDisplayTextComparer(ListSortDirection sortDirection, int columnIndex)
if (sortDirection == ListSortDirection.Descending)
SortModifier = -1;
SortModifier = 1;
_columnIndex = columnIndex;
#region IComparer<DataGridViewRow> Members
public int Compare(DataGridViewRow x, DataGridViewRow y)
int res = string.Compare(
return res * SortModifier;
#region IComparer Members
int IComparer.Compare(object x, object y)
return Compare(x as DataGridViewRow, y as DataGridViewRow);
In the real world, you use it by handling the column header clicking yourself for the column that requires a little help:
private void gridGroups_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
if (e.ColumnIndex != colGroupID.Index)
colGroupID.HeaderCell.SortGlyphDirection = SortOrder.None;
ListSortDirection sort = ListSortDirection.Ascending;
if (colGroupID.HeaderCell.SortGlyphDirection != SortOrder.None)
sort = colGroupID.HeaderCell.SortGlyphDirection == SortOrder.Ascending ?
ListSortDirection.Descending : ListSortDirection.Ascending;
gridGroups.Sort(new DataGridViewPlus.RowDisplayTextComparer(sort, colGroupID.Index));
colGroupID.HeaderCell.SortGlyphDirection = sort == ListSortDirection.Ascending ?
SortOrder.Ascending : SortOrder.Descending;
I'm always open to constructive criticism about better ways to code the solution - so if you are reading this and think its naff, let me know :)
Its also a little rough around the edges - no null value checking in the comparer for instance. But: