I believe this should be handled automatically. I am binding a DataGridView
to an array of objects:
public class Entity {
public string Name { get; set; }
public int PrimaryKey { get; set; }
}
Binding the grid:
public void BindGrid(Entity[] entities) {
grdEntities.DataSource = entities;
}
When I click the column head开发者_开发知识库er in the "Name" column, nothing is happening, even though the SortMode
is set to Automatic. The sort glyph is also missing from the column header.
I have tried binding to an IBindingList
and also an IList
but this did not work.
I am hoping there is a simple, elegant solution with setting properties on either the DataGridView
or the DataGridViewColumn
rather than having to make a new class to support sorting. What should I be doing to support sorting on a column by clicking the header on a DataBound DataGridView
?
I created a new IComparer based interface that allows you to specify both a column and a direction. I only did this because I need my sorting code to be as generic as possible - I have TWO grids that need to sort like this, and I don't want to maintain twice the code. Here is the interface, quite simple:
public interface IByColumnComparer : IComparer
{
string SortColumn { get; set; }
bool SortDescending { get; set; }
}
Obviously, if you're not worried about keeping things generic (you probably should) than this isn't strictly necessary. Then, I built a new class that is based on BindingList<>. This allowed me to override the sorting code and provide my own IByColumnComparer on a column by column basis which is what allowed for the flexibility I needed. Check this out:
public class SortableGenericCollection<T> : BindingList<T>
{
IByColumnComparer GenericComparer = null;
public SortableGenericCollection(IByColumnComparer SortingComparer)
{
GenericComparer = SortingComparer;
}
protected override bool SupportsSortingCore
{
get
{
return true;
}
}
protected override bool IsSortedCore
{
get
{
for (int i = 0; i < Items.Count - 1; ++i)
{
T lhs = Items[i];
T rhs = Items[i + 1];
PropertyDescriptor property = SortPropertyCore;
if (property != null)
{
object lhsValue = lhs == null ? null :
property.GetValue(lhs);
object rhsValue = rhs == null ? null :
property.GetValue(rhs);
int result;
if (lhsValue == null)
{
result = -1;
}
else if (rhsValue == null)
{
result = 1;
}
else
{
result = GenericComparer.Compare(lhs, rhs);
}
if (result >= 0)
{
return false;
}
}
}
return true;
}
}
private ListSortDirection sortDirection;
protected override ListSortDirection SortDirectionCore
{
get
{
return sortDirection;
}
}
private PropertyDescriptor sortProperty;
protected override PropertyDescriptor SortPropertyCore
{
get
{
return sortProperty;
}
}
protected override void ApplySortCore(PropertyDescriptor prop,
ListSortDirection direction)
{
sortProperty = prop;
sortDirection = direction;
GenericComparer.SortColumn = prop.Name;
GenericComparer.SortDescending = direction == ListSortDirection.Descending ? true : false;
List<T> list = (List<T>)Items;
list.Sort(delegate(T lhs, T rhs)
{
if (sortProperty != null)
{
object lhsValue = lhs == null ? null :
sortProperty.GetValue(lhs);
object rhsValue = rhs == null ? null :
sortProperty.GetValue(rhs);
int result;
if (lhsValue == null)
{
result = -1;
}
else if (rhsValue == null)
{
result = 1;
}
else
{
result = GenericComparer.Compare(lhs, rhs);
}
return result;
}
else
{
return 0;
}
});
}
protected override void RemoveSortCore()
{
sortDirection = ListSortDirection.Ascending;
sortProperty = null;
}
}
EDIT This should provide some information about how to create your own IComparer based on my interface above. The advantage to having your own IComparer based on the interface is that you can sort some columns one way, and other columns another (some columns might be strings, and some ints, some might have special rules about what goes on top, etc). Here is an example of how your IComparer might work:
public class MyGenericComparer : IByColumnComparer
{
private string columnToCompare;
private bool descending;
public string SortColumn
{
get { return columnToCompare; }
set { columnToCompare = value; }
}
public bool SortDescending
{
get { return descending; }
set { descending = value; }
}
public MyGenericComparer(string column, bool descend)
{
columnToCompare = column;
descending = descend;
}
public int Compare(object x, object y)
{
MyGenericObject firstObj = (MyGenericObject )x;
MyGenericObject secondObj = (MyGenericObject )y;
if (descending)
{
MyGenericObject tmp = secondObj ;
secondObj = firstObj ;
firstObj = tmp;
}
if (columnToCompare == "StringColumn")
{
//Run code to compare strings, return the appropriate int
//eg, "1" if firstObj was greater, "-1" is secondObj, "0" if equal
}
if (columnToCompare == "IntColumn")
{
//Run code to compare ints, return the appropriate int
//eg, "1" if firstObj was greater, "-1" is secondObj, "0" if equal
}
}
}
Then all you have to do is create your list with an instance of your comparer!
public static MyGenericComparer GridComparer = new MyGenericComparer();
public static SortableGenericCollection<GenericObject> GridList = new SortableGenericCollection<GenericObject>(GridComparer);
Might want to have a look at this question: DataGridView sort and e.g. BindingList<T> in .NET
The basic idea is that you have to extend BindingList<T>
and override ApplySortCore
in order to have column sorting working.
精彩评论