C# DataGridView 列排序与业务对象
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/280948/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
DataGridView Column sorting with Business Objects
提问by Sam Mackrill
I am setting-up my DataGridView like this:
我正在像这样设置我的 DataGridView:
jobs = new List<DisplayJob>();
uxJobList.AutoGenerateColumns = false;
jobListBindingSource.DataSource = jobs;
uxJobList.DataSource = jobListBindingSource;
int newColumn;
newColumn = uxJobList.Columns.Add("Id", "Job No.");
uxJobList.Columns[newColumn].DataPropertyName = "Id";
uxJobList.Columns[newColumn].DefaultCellStyle.Format = Global.JobIdFormat;
uxJobList.Columns[newColumn].DefaultCellStyle.Font = new Font(uxJobList.DefaultCellStyle.Font, FontStyle.Bold);
uxJobList.Columns[newColumn].AutoSizeMode = DataGridViewAutoSizeColumnMode.None;
uxJobList.Columns[newColumn].Width = 62;
uxJobList.Columns[newColumn].Resizable = DataGridViewTriState.False;
uxJobList.Columns[newColumn].SortMode = DataGridViewColumnSortMode.Automatic;
:
:
where the DisplayJob class looks like:
DisplayJob 类如下所示:
public class DisplayJob
{
public DisplayJob(int id)
{
Id = id;
}
public DisplayJob(JobEntity job)
{
Id = job.Id;
Type = job.JobTypeDescription;
CreatedAt = job.CreatedAt;
StartedAt = job.StartedAt;
ExternalStatus = job.ExternalStatus;
FriendlyExternalStatus = job.FriendlyExternalStatus;
ExternalStatusFriendly = job.ExternalStatusFriendly;
CustomerName = job.Customer.Name;
CustomerKey = job.Customer.CustomerKey;
WorkAddress = job.WorkAddress;
CreatedBy = job.CreatedBy;
CancelledAt = job.CancelledAt;
ClosedAt = job.ClosedAt;
ReasonWaiting = job.ReasonWaiting;
CancelledBy = job.CancelledBy;
CancelledReason = job.CancelledReason;
DisplayCreator = Global.GetDisplayName(CreatedBy);
ActionRedoNeeded = job.ActionRedoNeeded;
if (job.Scheme != null)
{
SchemeCode = job.Scheme.Code;
}
}
public int Id { get; private set; }
public string Type { get; private set; }
public DateTime CreatedAt { get; private set; }
public DateTime? StartedAt { get; private set; }
public string ExternalStatus { get; private set; }
public string FriendlyExternalStatus { get; private set; }
public string ExternalStatusFriendly { get; private set; }
public string CustomerName { get; private set; }
public string CustomerKey { get; private set; }
public string WorkAddress { get; private set; }
public string CreatedBy { get; private set; }
public DateTime? CancelledAt { get; private set; }
public DateTime? ClosedAt { get; private set; }
public string CancelledBy { get; private set; }
public string ReasonWaiting { get; private set; }
public string DisplayCreator { get; private set; }
public string CancelledReason { get; private set; }
public string SchemeCode { get; private set; }
public bool ActionRedoNeeded { get; private set; }
}
However the column sorting does not work. What is the best way to get this working?
但是列排序不起作用。让这个工作的最好方法是什么?
采纳答案by Patrick Desjardins
If you want to support sorting and searching on the collection, all it takes it to derive a class from your BindingList parameterized type, and override a few base class methods and properties.
如果您想支持对集合进行排序和搜索,只需从 BindingList 参数化类型派生一个类,并覆盖一些基类方法和属性即可。
The best way is to extend the BindingList and do those following things:
最好的方法是扩展 BindingList 并执行以下操作:
protected override bool SupportsSearchingCore
{
get
{
return true;
}
}
protected override bool SupportsSortingCore
{
get { return true; }
}
You will also need to implement the sort code:
您还需要实现排序代码:
ListSortDirection sortDirectionValue;
PropertyDescriptor sortPropertyValue;
protected override void ApplySortCore(PropertyDescriptor prop,
ListSortDirection direction)
{
sortedList = new ArrayList();
// Check to see if the property type we are sorting by implements
// the IComparable interface.
Type interfaceType = prop.PropertyType.GetInterface("IComparable");
if (interfaceType != null)
{
// If so, set the SortPropertyValue and SortDirectionValue.
sortPropertyValue = prop;
sortDirectionValue = direction;
unsortedItems = new ArrayList(this.Count);
// Loop through each item, adding it the the sortedItems ArrayList.
foreach (Object item in this.Items) {
sortedList.Add(prop.GetValue(item));
unsortedItems.Add(item);
}
// Call Sort on the ArrayList.
sortedList.Sort();
T temp;
// Check the sort direction and then copy the sorted items
// back into the list.
if (direction == ListSortDirection.Descending)
sortedList.Reverse();
for (int i = 0; i < this.Count; i++)
{
int position = Find(prop.Name, sortedList[i]);
if (position != i) {
temp = this[i];
this[i] = this[position];
this[position] = temp;
}
}
isSortedValue = true;
// Raise the ListChanged event so bound controls refresh their
// values.
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
else
// If the property type does not implement IComparable, let the user
// know.
throw new NotSupportedException("Cannot sort by " + prop.Name +
". This" + prop.PropertyType.ToString() +
" does not implement IComparable");
}
If you need more information you can always go there and get all explication about how to extend the binding list.
如果您需要更多信息,您可以随时前往那里获取有关如何扩展绑定列表的所有说明。
回答by Bruno Shine
I believe that your class must implement the IComparable
interface.
我相信你的类必须实现IComparable
接口。
Hope it helps,
希望能帮助到你,
Bruno Figueiredo
布鲁诺·菲格雷多
回答by Nicholas Piasecki
One of the easiest ways is to use the BindingListViewclass to wrap your list of DisplayJobs. The class implements some of the required interfaces that enable sorting and filtering in a DataGridView. That's the quick way. It works pretty well, though -- the only caveat is that if you cast things out of the DataGridView you need to cast to the wrapper object (ObjectView) instead of the actual item (DisplayJob).
最简单的方法之一是使用BindingListView类来包装您的 DisplayJobs 列表。该类实现了一些在 DataGridView 中启用排序和过滤所需的接口。这是快捷方式。不过,它工作得很好——唯一的警告是,如果您将内容从 DataGridView 中投射出来,您需要投射到包装器对象 (ObjectView) 而不是实际项目 (DisplayJob)。
The less lazy way is to create a custom collection time that implements IBindingList, implementing the sort methods there.
不那么懒惰的方法是创建一个实现 IBindingList 的自定义收集时间,在那里实现排序方法。
回答by Robert Rossney
Daok's solution is the right one. It's also very often more work than it's worth.
Daok 的解决方案是正确的。它也经常比它的价值更多的工作。
The lazy man's way to get the functionality you want is to create and populate a DataTable off of your business objects, and bind the DataGridView to that.
懒人获得您想要的功能的方法是从您的业务对象创建并填充一个 DataTable,然后将 DataGridView 绑定到它。
There are a lot of use cases that this approach won't handle (like, editing), and it obviously wastes time and space. As I said, it's lazy.
这种方法无法处理很多用例(例如,编辑),这显然会浪费时间和空间。正如我所说,它很懒惰。
But it's easy to write, and the resulting code is a damn sight less mysterious than an implementation of IBindingList
.
但是它很容易编写,而且生成的代码比IBindingList
.
Also, you're already writing a lot of the code anyway, or similar code at least: the code you write to define the DataTable frees you from having to write code to create the columns of the DataGridView, since the DataGridView will construct its columns off of the DataTable when you bind it.
此外,您已经编写了很多代码,或者至少是类似的代码:您编写的用于定义 DataTable 的代码使您不必编写代码来创建 DataGridView 的列,因为 DataGridView 将构造其列绑定 DataTable 时将其关闭。
回答by Martijn Boeker
The MS article suggested by Daok got me on the right track, but I wasn't satisfied with MSs implementation of SortableSearchableList. I find that implementation very strange and it didn't work well when there are duplicate values in a column. It also doesn't override IsSortedCore, which seems required by the DataGridView. If IsSortedCore is not overriden, the search glyph doesn't appear and toggling between ascending and descending doesn't work.
Daok 建议的 MS 文章让我走上了正轨,但我对 SortableSearchableList 的 MS 实现并不满意。我发现该实现非常奇怪,并且当列中有重复值时效果不佳。它也不会覆盖 IsSortedCore,这似乎是 DataGridView 所必需的。如果 IsSortedCore 未被覆盖,则不会出现搜索字形,并且无法在升序和降序之间切换。
See my version of SortableSearchableList below. In ApplySortCore() it sorts using a Comparison delegate set to an anonymous method. This version also supports setting custom comparisons for a particular property, which can be added by a derived class using AddCustomCompare().
请参阅下面我的 SortableSearchableList 版本。在 ApplySortCore() 中,它使用设置为匿名方法的比较委托进行排序。此版本还支持为特定属性设置自定义比较,可以由派生类使用 AddCustomCompare() 添加。
I'm not sure if the copyright notice still applies, but I just left it in.
我不确定版权声明是否仍然适用,但我只是把它留在了。
//---------------------------------------------------------------------
// Copyright (C) Microsoft Corporation. All rights reserved.
//
//THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//PARTICULAR PURPOSE.
//---------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Reflection;
using System.Collections;
namespace SomethingSomething
{
/// <summary>
/// Supports sorting of list in data grid view.
/// </summary>
/// <typeparam name="T">Type of object to be displayed in data grid view.</typeparam>
public class SortableSearchableList<T> : BindingList<T>
{
#region Data Members
private ListSortDirection _sortDirectionValue;
private PropertyDescriptor _sortPropertyValue = null;
/// <summary>
/// Dictionary from property name to custom comparison function.
/// </summary>
private Dictionary<string, Comparison<T>> _customComparisons = new Dictionary<string, Comparison<T>>();
#endregion
#region Constructors
/// <summary>
/// Default constructor.
/// </summary>
public SortableSearchableList()
{
}
#endregion
#region Properties
/// <summary>
/// Indicates if sorting is supported.
/// </summary>
protected override bool SupportsSortingCore
{
get
{
return true;
}
}
/// <summary>
/// Indicates if list is sorted.
/// </summary>
protected override bool IsSortedCore
{
get
{
return _sortPropertyValue != null;
}
}
/// <summary>
/// Indicates which property the list is sorted.
/// </summary>
protected override PropertyDescriptor SortPropertyCore
{
get
{
return _sortPropertyValue;
}
}
/// <summary>
/// Indicates in which direction the list is sorted on.
/// </summary>
protected override ListSortDirection SortDirectionCore
{
get
{
return _sortDirectionValue;
}
}
#endregion
#region Methods
/// <summary>
/// Add custom compare method for property.
/// </summary>
/// <param name="propertyName"></param>
/// <param name="compareProperty"></param>
protected void AddCustomCompare(string propertyName, Comparison<T> comparison)
{
_customComparisons.Add(propertyName, comparison);
}
/// <summary>
/// Apply sort.
/// </summary>
/// <param name="prop"></param>
/// <param name="direction"></param>
protected override void ApplySortCore(PropertyDescriptor prop, ListSortDirection direction)
{
Comparison<T> comparison;
if (!_customComparisons.TryGetValue(prop.Name, out comparison))
{
// Check to see if the property type we are sorting by implements
// the IComparable interface.
Type interfaceType = prop.PropertyType.GetInterface("IComparable");
if (interfaceType != null)
{
comparison = delegate(T t1, T t2)
{
IComparable val1 = (IComparable)prop.GetValue(t1);
IComparable val2 = (IComparable)prop.GetValue(t2);
return val1.CompareTo(val2);
};
}
else
{
// Last option: convert to string and compare.
comparison = delegate(T t1, T t2)
{
string val1 = prop.GetValue(t1).ToString();
string val2 = prop.GetValue(t2).ToString();
return val1.CompareTo(val2);
};
}
}
if (comparison != null)
{
// If so, set the SortPropertyValue and SortDirectionValue.
_sortPropertyValue = prop;
_sortDirectionValue = direction;
// Create sorted list.
List<T> _sortedList = new List<T>(this);
_sortedList.Sort(comparison);
// Reverse order if needed.
if (direction == ListSortDirection.Descending)
{
_sortedList.Reverse();
}
// Update list.
int count = this.Count;
for (int i = 0; i < count; i++)
{
this[i] = _sortedList[i];
}
// Raise the ListChanged event so bound controls refresh their
// values.
OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
}
}
// Method below was in the original implementation from MS. Don't know what it's for.
// -- Martijn Boeker, Jan 21, 2010
//protected override void RemoveSortCore()
//{
// //int position;
// //object temp;
// //// Ensure the list has been sorted.
// //if (unsortedItems != null)
// //{
// // // Loop through the unsorted items and reorder the
// // // list per the unsorted list.
// // for (int i = 0; i < unsortedItems.Count; )
// // {
// // position = this.Find(SortPropertyCore.Name,
// // unsortedItems[i].GetType().
// // GetProperty(SortPropertyCore.Name).
// // GetValue(unsortedItems[i], null));
// // if (position >= 0 && position != i)
// // {
// // temp = this[i];
// // this[i] = this[position];
// // this[position] = (T)temp;
// // i++;
// // }
// // else if (position == i)
// // i++;
// // else
// // // If an item in the unsorted list no longer exists, delete it.
// // unsortedItems.RemoveAt(i);
// // }
// // OnListChanged(new ListChangedEventArgs(ListChangedType.Reset, -1));
// //}
//}
/// <summary>
/// Ability to search an item.
/// </summary>
protected override bool SupportsSearchingCore
{
get
{
return true;
}
}
/// <summary>
/// Finds an item in the list.
/// </summary>
/// <param name="prop"></param>
/// <param name="key"></param>
/// <returns></returns>
protected override int FindCore(PropertyDescriptor prop, object key)
{
// Implementation not changed from MS example code.
// Get the property info for the specified property.
PropertyInfo propInfo = typeof(T).GetProperty(prop.Name);
T item;
if (key != null)
{
// Loop through the the items to see if the key
// value matches the property value.
for (int i = 0; i < Count; ++i)
{
item = (T)Items[i];
if (propInfo.GetValue(item, null).Equals(key))
return i;
}
}
return -1;
}
/// <summary>
/// Finds an item in the list.
/// </summary>
/// <param name="prop"></param>
/// <param name="key"></param>
/// <returns></returns>
private int Find(string property, object key)
{
// Implementation not changed from MS example code.
// Check the properties for a property with the specified name.
PropertyDescriptorCollection properties =
TypeDescriptor.GetProperties(typeof(T));
PropertyDescriptor prop = properties.Find(property, true);
// If there is not a match, return -1 otherwise pass search to
// FindCore method.
if (prop == null)
return -1;
else
return FindCore(prop, key);
}
#endregion
}
}
回答by CXRom
Martijn excelent code but only one detail u need to validate null cells or empty :)
Martijn 优秀的代码,但您只需要验证空单元格或空单元格的一个细节:)
if (!_customComparisons.TryGetValue(prop.Name, out comparison))
{
// Check to see if the property type we are sorting by implements
// the IComparable interface.
Type interfaceType = prop.PropertyType.GetInterface("IComparable");
if (interfaceType != null)
{
comparison = delegate(T t1, T t2)
{
IComparable val1 = (IComparable)prop.GetValue(t1) ?? "";
IComparable val2 = (IComparable)prop.GetValue(t2) ?? "";
return val1.CompareTo(val2);
};
}
else
{
// Last option: convert to string and compare.
comparison = delegate(T t1, T t2)
{
string val1 = (prop.GetValue(t1) ?? "").ToString();
string val2 = (prop.GetValue(t2) ?? "").ToString();
return val1.CompareTo(val2);
};
}
}
That's all luck
这都是运气
回答by Joe H
I'd recommend replacing:
我建议更换:
jobs = new List<DisplayJob>();
with:
和:
jobs = new SortableBindingList<DisplayJob>();
The code for SortableBindingList is here: http://www.timvw.be/presenting-the-sortablebindinglistt/
SortableBindingList 的代码在这里:http: //www.timvw.be/presenting-the-sortablebindinglistt/
I've used code based on this in production without any problems. It's only limitation is that it is not a stable sort.
我在生产中使用了基于此的代码,没有任何问题。唯一的限制是它不是一种稳定的排序。
If you want the sort to be stable, replace:
如果您希望排序稳定,请替换:
itemsList.Sort(delegate(T t1, T t2)
{
object value1 = prop.GetValue(t1);
object value2 = prop.GetValue(t2);
return reverse * Comparer.Default.Compare(value1, value2);
});
with an insertion sort:
使用插入排序:
int j;
T index;
for (int i = 0; i < itemsList.Count; i++)
{
index = itemsList[i];
j = i;
while ((j > 0) && (reverse * Comparer.Default.Compare(prop.GetValue(itemsList[j - 1]), prop.GetValue(index)) > 0))
{
itemsList[j] = itemsList[j - 1];
j = j - 1;
}
itemsList[j] = index;
}
回答by grabah
Did you tried setting SortMemberPath for every column?
您是否尝试为每一列设置 SortMemberPath?
uxJobList.Columns[newColumn].SortMemberPath="Id";
uxJobList.Columns[newColumn].SortMemberPath="Id";
and instead of List im just using ObservableCollection
而不是 List 我只是使用 ObservableCollection