使用通用列表作为基础源的 C# DataGridView 排序
Posted
技术标签:
【中文标题】使用通用列表作为基础源的 C# DataGridView 排序【英文标题】:C# DataGridView sorting with Generic List as underlying source 【发布时间】:2009-09-04 06:34:09 【问题描述】:我正在使用 Windows 窗体 DataGridView 来显示 MyObject
对象的通用列表。
首先我把这个集合包装成一个BindingSource
集合,然后:
dataGridView.DataSource = myBindingSource;
我想要做的是允许用户通过单击代表 MyObject 中的具体属性的列的标题来对列进行排序。
我已经阅读了一些我应该在绑定之前进行排序的文章。但是,如果我想实时对列进行排序,这对我没有帮助,因为它已经绑定了。
问题是,我到底需要做什么,所以我可以在 DataGridView 中看到排序箭头,并且我可以对每一列进行排序?
【问题讨论】:
见***.com/questions/3770857/…。就像dataGridView.DataSource = new BindingListView<MyObject>(myObjects.ToList())
一样简单,库位于sourceforge.net/projects/blw。
【参考方案1】:
对datagridview的数据源为泛型List的列进行排序的完整代码
//-----------------------------------------------------------------------------------------
//In the form - In constructor or form load, populate the grid.
//--------------------------------------------------------------------------------------------
List<student> students;
private void PopulateList()
student std1 = new student("sss", 15, "Female");
student std2 = new student("ddd", 12, "Male");
student std3 = new student("zzz", 16, "Male");
student std4 = new student("qqq", 14, "Female");
student std5 = new student("aaa", 11, "Male");
student std6 = new student("lll", 13, "Female");
students = new List<student>();
students.Add(std1);
students.Add(std2);
students.Add(std3);
students.Add(std4);
students.Add(std5);
students.Add(std6);
dataGridView1.DataSource = students;
//---------------------------------------------------------------------------------------------
//Comparer class to perform sorting based on column name and sort order
//---------------------------------------------------------------------------------------------
class StudentComparer : IComparer<Student>
string memberName = string.Empty; // specifies the member name to be sorted
SortOrder sortOrder = SortOrder.None; // Specifies the SortOrder.
/// <summary>
/// constructor to set the sort column and sort order.
/// </summary>
/// <param name="strMemberName"></param>
/// <param name="sortingOrder"></param>
public StudentComparer(string strMemberName, SortOrder sortingOrder)
memberName = strMemberName;
sortOrder = sortingOrder;
/// <summary>
/// Compares two Students based on member name and sort order
/// and return the result.
/// </summary>
/// <param name="Student1"></param>
/// <param name="Student2"></param>
/// <returns></returns>
public int Compare(Student Student1, Student Student2)
int returnValue = 1;
switch (memberName)
case "Name" :
if (sortOrder == SortOrder.Ascending)
returnValue = Student1.Name.CompareTo(Student2.Name);
else
returnValue = Student2.Name.CompareTo(Student1.Name);
break;
case "Sex":
if (sortOrder == SortOrder.Ascending)
returnValue = Student1.Sex.CompareTo(Student2.Sex);
else
returnValue = Student2.Sex.CompareTo(Student1.Sex);
break;
default:
if (sortOrder == SortOrder.Ascending)
returnValue = Student1.Name.CompareTo(Student2.Name);
else
returnValue = Student2.Name.CompareTo(Student1.StudentId);
break;
return returnValue;
//---------------------------------------------------------------------------------------------
// Performing sort on click on Column Header
//---------------------------------------------------------------------------------------------
private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
//get the current column details
string strColumnName = dataGridView1.Columns[e.ColumnIndex].Name;
SortOrder strSortOrder = getSortOrder(e.ColumnIndex);
students.Sort(new StudentComparer(strColumnName, strSortOrder));
dataGridView1.DataSource = null;
dataGridView1.DataSource = students;
customizeDataGridView();
dataGridView1.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = strSortOrder;
/// <summary>
/// Get the current sort order of the column and return it
/// set the new SortOrder to the columns.
/// </summary>
/// <param name="columnIndex"></param>
/// <returns>SortOrder of the current column</returns>
private SortOrder getSortOrder(int columnIndex)
if (dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection == SortOrder.None ||
dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection == SortOrder.Descending)
dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection = SortOrder.Ascending;
return SortOrder.Ascending;
else
dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection = SortOrder.Descending;
return SortOrder.Descending;
【讨论】:
很好的解决方案,非常感谢!不过,我花时间对其进行了一些改进。【参考方案2】:我很难相信网格不提供开箱即用的基本排序,不需要代码。毕竟,必须处理标题单击事件并调用 DataGridView.Sort 指示列(由单击的内容确定,由网格跟踪)和排序方向(由当前的排序状态确定,由网格跟踪)是非常愚蠢的)。
为什么没有简单的 SortMode 或 AllowUserToSort 属性在默认情况下执行完全相同的操作?
我已将我的网格绑定到一个列表,并且我已将列映射到的属性都是基本类型,如字符串、int、DateTime 等。所有这些都是 IComparable。那么到底为什么我需要写一行代码呢?特别是考虑到文档如下:
默认情况下,用户可以对数据进行排序 通过单击 DataGridView 控件 文本框列的标题。
MSDN
那是 Framework 3.0 文档,我的目标是 3.5,但“其他版本”都是指 Visual Studio 的版本,而不是 Framework 的版本。微软到底是怎么回事?!?
【讨论】:
顺便说一句,我的列都是 DataGridViewTextBoxColumn 类型 - 这大概是 MS 在说“文本框列”时的意思。 有一个 SortMode 但它似乎什么也没做!【参考方案3】:这篇文章“Presenting the SortableBindingList”中的一个很好的解决方案: http://www.timvw.be/2007/02/22/presenting-the-sortablebindinglistt/
【讨论】:
【参考方案4】:这是一个使用 Reflection 和 Linq 按列排序的更简单的解决方案。 dataGridView1 的 DataSource 设置为 compareList,声明为:
private List<CompareInfo> compareList;
private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
string strColumnName = dataGridView1.Columns[e.ColumnIndex].Name;
SortOrder strSortOrder = getSortOrder(e.ColumnIndex);
if (strSortOrder == SortOrder.Ascending)
compareList = compareList.OrderBy(x => typeof(CompareInfo).GetProperty(strColumnName).GetValue(x, null)).ToList();
else
compareList = compareList.OrderByDescending(x => typeof(CompareInfo).GetProperty(strColumnName).GetValue(x, null)).ToList();
dataGridView1.DataSource = compareList;
dataGridView1.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = strSortOrder;
private SortOrder getSortOrder(int columnIndex)
if (dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection == SortOrder.None ||
dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection == SortOrder.Descending)
dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection = SortOrder.Ascending;
return SortOrder.Ascending;
else
dataGridView1.Columns[columnIndex].HeaderCell.SortGlyphDirection = SortOrder.Descending;
return SortOrder.Descending;
public class CompareInfo
public string FileName get; set;
public string UAT_Folder get; set;
public string UAT_Path
get return UAT_Folder + FileName;
public string PROD_Folder get; set;
public string PROD_Path
get return PROD_Folder + FileName;
【讨论】:
我稍微修改了这个解决方案以获得更快的结果。我使用 DataPropertyName 而不是 Name 作为 strColumnName,并将 compareList 投射到顶部的 click 事件中: var compareList = (dataGridView1.DataSource as List我的解决方案是这样的:
我自己使用 myBindingSource,我在单独的线程中进行排序、分组..任何事情。 然后我只是将结果绑定到 DataGridView。
myDataGridView.DataSource = bindingSource;
为此,我已将所有列设置为排序'Programatically'
(在设计器中)
然后我手动添加箭头(ASCENDING / DESCENDING)
通过设置
cell.SortGlyphDirection = ... ;
在后面的代码中。
【讨论】:
【参考方案6】:如果创建自己的用户控件更可取,您可以使用以下代码创建自定义排序方法:
private string _lastSortColumn;
private ListSortDirection _lastSortDirection;
public void Sort(DataGridViewColumn column)
// Flip sort direction, if the column chosen was the same as last time
if (column.Name == _lastSortColumn)
_lastSortDirection = 1 - _lastSortDirection;
// Otherwise, reset the sort direction to its default, ascending
else
_lastSortColumn = column.Name;
_lastSortDirection = ListSortDirection.Ascending;
// Prep data for sorting
var data = (IEnumerable<dynamic>)DataSource;
var orderProperty = column.DataPropertyName;
// Sort data
if (_lastSortDirection == ListSortDirection.Ascending)
DataSource = data.OrderBy(x => x.GetType().GetProperty(orderProperty).GetValue(x, null)).ToList();
else
DataSource = data.OrderByDescending(x => x.GetType().GetProperty(orderProperty).GetValue(x, null)).ToList();
// Set direction of the glyph
Columns[column.Index].HeaderCell.SortGlyphDirection
= _lastSortDirection == ListSortDirection.Ascending
? SortOrder.Ascending : SortOrder.Descending;
然后你可以重写标题点击方法来调用你的排序函数:
protected override void OnColumnHeaderMouseClick(DataGridViewCellMouseEventArgs e)
base.OnColumnHeaderMouseClick(e);
var column = Columns[e.ColumnIndex];
if (column.SortMode == DataGridViewColumnSortMode.Automatic
|| column.SortMode == DataGridViewColumnSortMode.NotSortable)
Sort(column);
【讨论】:
【参考方案7】:您可能还想看看这篇文章,您可以在其中获得两个有趣的链接来实现自定义的 SortableBindingList:
Sort Datagridview columns when datasource binded to List(Of T)
【讨论】:
【参考方案8】:看这篇文章
http://msdn.microsoft.com/en-us/library/0868ft3z.aspx
通过阅读它,我看到了“此方法通过比较指定列中的值对 DataGridView 的内容进行排序。默认情况下,排序操作将使用 Compare 方法使用 DataGridViewCell 比较列中的单元格对。 ::.Value 属性。”
最好的问候, 约旦
【讨论】:
【参考方案9】:在绑定到 List 时解决 DataGridView 排序问题的另一个选项是,如果您不处理庞大的数据集,那么您可能可以尝试将 List 转换为 DataTable,然后将生成的 DataTable 绑定到 BindingSource/DataGridView .
这将需要 IComparer 的自定义实现。就我而言,我正在处理一个较小的列表,但要显示更多字段。所以实现 IComparer 意味着编写太多样板代码。
查看将列表转换为数据表的简洁方法:https://***.com/a/34062898/4534493
【讨论】:
【参考方案10】:首先我使用了 System.Reflection; 然后:写这个方法
public DataTable ToDataTable<T>(List<T> items)
DataTable dataTable = new DataTable(typeof(T).Name);
//Get all the properties
PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public |
BindingFlags.Instance);
foreach (PropertyInfo prop in Props)
//Setting column names as Property names
dataTable.Columns.Add(prop.Name);
foreach (T item in items)
var values = new object[Props.Length];
for (int i = 0; i < Props.Length; i++)
//inserting property values to datatable rows
values[i] = Props[i].GetValue(item, null);
dataTable.Rows.Add(values);
//put a breakpoint here and check datatable
return dataTable;
然后调用方法: DataTable dt = ToDataTable(lst.ToList());
【讨论】:
【参考方案11】:使用 DataSource 对 DataGridView 列进行通用排序
private void ItemsTable_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
var sortingColumn = ItemsTable.Columns[e.ColumnIndex];
var propertyInfo = typeof(ListingInfo).GetProperty(sortingColumn.DataPropertyName);
if (propertyInfo == null) // ignore columns with buttons or pictures
return;
foreach (DataGridViewColumn dataGridViewColumn in ItemsTable.Columns)
if (dataGridViewColumn != sortingColumn)
dataGridViewColumn.HeaderCell.SortGlyphDirection = SortOrder.None;
sortingColumn.HeaderCell.SortGlyphDirection = sortingColumn.HeaderCell.SortGlyphDirection == SortOrder.Ascending
? SortOrder.Descending : SortOrder.Ascending;
_listingsList.Sort((x, y) =>
var first = sortingColumn.HeaderCell.SortGlyphDirection == SortOrder.Ascending ? x : y;
var second = sortingColumn.HeaderCell.SortGlyphDirection == SortOrder.Ascending ? y : x;
var firstValue = propertyInfo.GetValue(first);
var secondValue = propertyInfo.GetValue(second);
if (firstValue == null)
return secondValue == null ? 0 : -1;
return secondValue == null ? 1 : ((IComparable)firstValue).CompareTo(secondValue);
);
// tell the binding list to raise a list change event so that bound controls reflect the new item order
ItemsTable.ResetBindings();
ItemsTable.Refresh();
【讨论】:
以上是关于使用通用列表作为基础源的 C# DataGridView 排序的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 json.net 将 c# 通用列表转换为 json?