I would like to sort my grid at form load but Visual Studio is not liking my code. Any help would be appreciated!
dataGridView1.Sort(dataGridView1.Columns[0], ListSortDirection.Descending);
You could sort the data initially before binding it. For example, if you have a DataTable, you could do:
dataTable.DefaultView.Sort("Cola DESC");
And then you bind your data already sorted.
private void Form_Load(object sender, EventArgs e)
{
//bind u r collection to datagridview
DataGridView1.Sort( new RowComparer( SortOrder.Ascending ) );
}
private class RowComparer : System.Collections.IComparer
{
private static int sortOrderModifier = 1;
public RowComparer(SortOrder sortOrder)
{
if (sortOrder == SortOrder.Descending)
{
sortOrderModifier = -1;
}
else if (sortOrder == SortOrder.Ascending)
{
sortOrderModifier = 1;
}
}
public int Compare(object x, object y)
{
DataGridViewRow DataGridViewRow1 = (DataGridViewRow)x;
DataGridViewRow DataGridViewRow2 = (DataGridViewRow)y;
// Try to sort based on the Last Name column.
int CompareResult = System.String.Compare(
DataGridViewRow1.Cells[1].Value.ToString(),
DataGridViewRow2.Cells[1].Value.ToString());
// If the Last Names are equal, sort based on the First Name.
if ( CompareResult == 0 )
{
CompareResult = System.String.Compare(
DataGridViewRow1.Cells[0].Value.ToString(),
DataGridViewRow2.Cells[0].Value.ToString());
}
return CompareResult * sortOrderModifier;
}
}
I got it to work by adding this line to the BindingSource
this.carCheckoutBindingSource.Sort = "startMiles DESC";
Is the datagridview bound to a collection? If Yes then perform the sort on the collection and rebind the sorted collection to the datagridview
Ex:
collection.OrderBy(i=> i.<PropertyName>);
datagridview.DataSource = collection;
NOTE:
My collection is of type List.
Related
I have an assignment about ListView sort by Column using C# Windows Form and the codes that I got from MSDN didn't work. Can anybody find out what's wrong with the codes? Everytime I click the ListView Column nothing happens.
Here's the code, I also added the items that will show in my ListView
private int sortColumn = -1;
private void listView1_ColumnClick(object sender, ColumnClickEventArgs e)
{
// Determine whether the column is the same as the last column clicked.
if (e.Column != sortColumn)
{
// Set the sort column to the new column.
sortColumn = e.Column;
// Set the sort order to ascending by default.
listView1.Sorting = SortOrder.Ascending;
}
else
{
// Determine what the last sort order was and change it.
if (listView1.Sorting == SortOrder.Ascending)
listView1.Sorting = SortOrder.Descending;
else
listView1.Sorting = SortOrder.Ascending;
}
// Call the sort method to manually sort.
listView1.Sort();
// Set the ListViewItemSorter property to a new ListViewItemComparer
// object.
this.listView1.ListViewItemSorter = new ListViewItemComparer(e.Column,
listView1.Sorting);
}
private void FillItems()
{
// Add items
ListViewItem item1 = new ListViewItem("Nipun Tomar");
item1.SubItems.Add("1");
item1.SubItems.Add("10/11/2000");
ListViewItem item2 = new ListViewItem("First Last");
item2.SubItems.Add("2");
item2.SubItems.Add("12/12/2010");
ListViewItem item3 = new ListViewItem("User User");
item3.SubItems.Add("3");
item3.SubItems.Add("12/01/1800");
ListViewItem item4 = new ListViewItem("Sample");
item4.SubItems.Add("4");
item4.SubItems.Add("05/30/1900");
// Add the items to the ListView.
listView1.Items.AddRange(
new ListViewItem[] {item1, item2, item3, item4});
}
private void Form1_Load(object sender, EventArgs e)
{
FillItems();
}
public class ListViewItemComparer : IComparer
{
private int col;
private SortOrder order;
public ListViewItemComparer()
{
col = 0;
order = SortOrder.Ascending;
}
public ListViewItemComparer(int column, SortOrder order)
{
col = column;
this.order = order;
}
public int Compare(object x, object y)
{
int returnVal= -1;
returnVal = String.Compare(((ListViewItem)x).SubItems[col].Text,
((ListViewItem)y).SubItems[col].Text);
// Determine whether the sort order is descending.
if (order == SortOrder.Descending)
// Invert the value returned by String.Compare.
returnVal *= -1;
return returnVal;
}
}
Note: I added the columns in the design form.
Here's what my assignment looks like:
You dont have any columns in your list view. They are just items. thats why the event listView1_ColumnClick never fires. (also make sure you have added this event to your list view.)
Add this at first of your Form1_Load event to initialize columns.
// set view mode to see columns
listView1.View = View.Details;
// 100 is just a length of column. HorizontalAlignment.Left starts from left side
listView1.Columns.Add("Name", 100, HorizontalAlignment.Left);
listView1.Columns.Add("Number", 100, HorizontalAlignment.Left);
listView1.Columns.Add("Date", 100, HorizontalAlignment.Left);
Now you see the columns which you can select them to sort items by that column.
Note that i just added 3 columns. so list view will show each item with 2 of their SubItems under columns by order.
As you requested to post the gif. Here is it :)
You call listView1.Sort()before setting the comparer: this.listView1.ListViewItemSorter = ...
Just invert the two lines.
Also, note that you are using string.Compare for all columns, which, I think, it's not what you want for column 3 (date)
[Edit]:
Just realized now the setting the value for ListviewItemSorter cause the LV to sort: your code seems to work even without calling listView1.Sort()
Problem must be somewhere else. Try with debugger setting breakpoints...
private void lvw_ColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e)
{
ListViewColumnSorter sorter = new ListViewColumnSorter();
sorter.SortColumn = e.Column;
sorter.Order = System.Windows.Forms.SortOrder.Ascending;
lvw.ListViewItemSorter =sorter;
lvw.Sort();
}
How do I sort a DataGridView by two columns (ascending)? I have two columns: day and status.
If I need to sort by one column, I do:
this.dataGridView1.Sort (this.dataGridView1.Columns["day"], ListSortDirection.Ascending);
But for two?
If your DataGridView is databound, you can sort your Datatable view and rebind to datatable as below:
private DataGridView dataGridView1 = new DataGridView();
private BindingSource bindingSource1 = new BindingSource();
private void Form1_Load(object sender, System.EventArgs e)
{
// Bind the DataGridView to the BindingSource
dataGridView1.DataSource = bindingSource1;
SortDataByMultiColumns(); //Sort the Data
}
private void SortDataByMultiColumns()
{
DataView view = dataTable1.DefaultView;
view.Sort = "day ASC, status DESC";
bindingSource1.DataSource = view; //rebind the data source
}
OR, without using bindingsource and binding directly to DataView:
private void SortDataByMultiColumns()
{
DataView view = ds.Tables[0].DefaultView;
view.Sort = "day ASC, status DESC";
dataGridView1.DataSource = view; //rebind the data source
}
Add a hidden column that combines the two and sort by that.
You can use the DataGridView's Sort method, but specify an argument that is an instance of a class that implements IComparer.
Here is an example of such a class:
public class MyTwoColumnComparer : System.Collections.IComparer
{
private string _SortColumnName1;
private int _SortOrderMultiplier1;
private string _SortColumnName2;
private int _SortOrderMultiplier2;
public MyTwoColumnComparer(string pSortColumnName1, SortOrder pSortOrder1, string pSortColumnName2, SortOrder pSortOrder2)
{
_SortColumnName1 = pSortColumnName1;
_SortOrderMultiplier1 = (pSortOrder1 == SortOrder.Ascending) ? 1 : -1;
_SortColumnName2 = pSortColumnName2;
_SortOrderMultiplier2 = (pSortOrder2 == SortOrder.Ascending) ? 1 : -1;
}
public int Compare(object x, object y)
{
DataGridViewRow r1 = (DataGridViewRow)x;
DataGridViewRow r2 = (DataGridViewRow)y;
int iCompareResult = _SortOrderMultiplier1 * String.Compare(r1.Cells[_SortColumnName1].Value.ToString(), r2.Cells[_SortColumnName1].Value.ToString());
if (iCompareResult == 0) iCompareResult = _SortOrderMultiplier2 * String.Compare(r1.Cells[_SortColumnName2].Value.ToString(), r2.Cells[_SortColumnName2].Value.ToString());
return iCompareResult;
}
}
Now, we might call this from a column whose SortMode is 'Programmatic' on a mouse click:
private void dgvAllMyEmployees_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
DataGridViewColumn dgvcClicked = dgvAllEmployees.Columns[e.ColumnIndex];
if (dgvcClicked.SortMode == DataGridViewColumnSortMode.Programmatic)
{
_SortOrder = (_SortOrder == SortOrder.Ascending) ? SortOrder.Descending : SortOrder.Ascending;
MyTwoColumnComparer Sort2C = new MyTwoColumnComparer(dgvcClicked.Name, _SortOrder, "LastName", SortOrder.Ascending);
dgvAllEmployees.Sort(Sort2C);
}
}
The class level variable _SortOrder helps keep track of which order to go in. One can enhance this more to remember the last two columns clicked and sort on them in the desired order.
TLDR; I have a two-line solution.
I had to do the same thing, but after researching all these complicated ways to do this by either including a separate .dll or writing my own class/methods, I knew there had to be an easier way. It turns out I was right because I figured out how to accomplish this with using only two lines of code. This worked for me.
Luckily, it turns out for us that the .NET Framework Sort() method does help us with this. The idea is that you want to sort the columns individually, but the order in which you sort them in is what will produce the desired output.
So, as an example, I have a column for file type and a column for a file name. Whenever I want to sort the data by the types, I want to make sure that the names are also sorted within each type shown.
GOAL: Sorting by type will also sort the file names alphabetically.
Data:
zxcv.css
testimg3.jpg
asdf.html
testimg2.jpg
testimg1.jpg
Sorting data by name:
mConflictsDataGridView.Sort(mConflictsDataGridView.Columns[mNameLabel.Index], ListSortDirection.Ascending);
asdf.html
testimg1.jpg
testimg2.jpg
testimg3.jpg
zxcv.css
As you can see, this will name sure that the names will be sorted accordingly, such that when I now sort by the file types, both requirements will satisfy.
Sorting data by file type:
mConflictsDataGridView.Sort(mConflictsDataGridView.Columns[mFileExtensionLabel.Index], ListSortDirection.Ascending);
zxcv.css
asdf.html
testimg1.jpg
testimg2.jpg
testimg3.jpg
Voila! It's sorted!
SOLUTION: In your case, you may want to try something like the following, and you may need to tweak it some more to have it cater to your own code.
DataGridView1.Sort(DataGridView1.Columns["status"], ListSortDirection.Ascending);
DataGridView1.Sort(DataGridView1.Columns["day"], ListSortDirection.Asscending);
This should be able to display your results by the day with its status field sorted as well.
The answer that John Kurtz provided got me close. But what I found was the when I clicked on a column once, it did indeed sort by the two columns ... In his example: dgvcClicked.Name, "LastName". So, good!!
But, if I clicked on the column again, then it would NOT sort by the opposite direction. So the column became stuck in Ascending.
To overcome this, I had to track the sort order manually. Started with this class:
public class ColumnSorting
{
public int ColumnIndex { get; set; }
public ListSortDirection Direction { get; set; }
}
Then, I added this globally scoped List:
List<ColumnSorting> _columnSortingList = new List<ColumnSorting>();
Then, in the method that does the Sort, I would
Check to see if the column index being sorted already exists in _columnSortingList. If not, add it.
If it already exists, then swap the sort order
And Bob's your uncle.
You can try this, or use custom sorting:
private void dataGridView1_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if (dataGridView1.Columns[e.ColumnIndex].HeaderText =="day")
{
myBindingSource.Sort = "day, hour";
}
}
I use this solution when working with bound data. This works for our users, and displays the current sort criteria. All sorts are only in ascending order.
Add a CheckBox, a TextBox, a ColumnHeaderMouseClick event, and code as shown. The CheckBox will toggle the TextBox's visibility, and clicking on any column header will add the sort criteria to the TextBox. To clear the TextBox, just double click in it.
private void FooDataGridView_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
{
if(chkMultiSort.Checked == true)
{
string columnHeader = FooDataGridView.Columns[e.ColumnIndex].DataPropertyName;
txtMultiSort.Text += (columnHeader + ", ");
try
{
FooBindingSource.Sort = txtMultiSort.Text.Remove(txtMultiSort.Text.Length - 2);
}
catch
{
MessageBox.Show("Invalid Sort Data", "Information", MessageBoxButtons.OK, MessageBoxIcon.None);
txtMultiSort.Text = String.Empty;
}
}
}
private void ChkMultiSort_CheckedChanged(object sender, EventArgs e)
{
if(chkMultiSort.Checked == true)
{
txtMultiSort.Visible = true;
}
else
{
txtMultiSort.Visible = false;
txtMultiSort.Text = String.Empty;
}
}
private void TxtMultiSort_DoubleClick(object sender, EventArgs e)
{
txtMultiSort.Text = String.Empty;
}
Here is a refinement on the IComparer class from #John Kurtz, which supports mutliple columns and sorting by numbers or dates.
public static void SortOnMultipleColumns(DataGridView dgv, Dictionary<string /*Column Name*/, ColumnSortInfo> sortingColumns)
{
// Show the glyphs
foreach (DataGridViewColumn col in dgv.Columns)
{
System.Windows.Forms.SortOrder sortOrder = System.Windows.Forms.SortOrder.None;
foreach (var kvp in sortingColumns)
{
if (kvp.Key == col.Name)
{
sortOrder = kvp.Value.SortOrder;
break;
}
}
col.HeaderCell.SortGlyphDirection = sortOrder;
}
// Sort the grid
MultiColumnCompararor multiColumnCompararor = new MultiColumnCompararor(sortingColumns);
dgv.Sort(multiColumnCompararor);
}
public class ColumnSortInfo
{
public enum ValueConversion { ToString, ToNumber, ToDate}
public ColumnSortInfo(System.Windows.Forms.SortOrder sortOrder, ValueConversion valueConversion = ValueConversion.ToString)
{
SortOrder = sortOrder;
MyValueConversion = valueConversion;
SortOrderMultiplier = (SortOrder == SortOrder.Ascending) ? 1 : -1;
}
public System.Windows.Forms.SortOrder SortOrder { get; set; }
public int SortOrderMultiplier { get; }
public ValueConversion MyValueConversion { get; set; }
public static double StringToDouble(string sVal)
{
if (Double.TryParse(sVal, out double dVal))
{
return dVal;
}
return 0;
}
public static DateTime StringToDateTime(string sVal)
{
if (DateTime.TryParse(sVal, out DateTime dt))
{
return dt;
}
return DateTime.MinValue;
}
}
private class MultiColumnCompararor : System.Collections.IComparer
{
IDictionary<string /*Column Name*/, ColumnSortInfo> _sortingColumns;
public MultiColumnCompararor(IDictionary<string /*Column Name*/, ColumnSortInfo> sortingColumns)
{
_sortingColumns = sortingColumns;
}
public int Compare(object x, object y)
{
try
{
DataGridViewRow r1 = (DataGridViewRow)x;
DataGridViewRow r2 = (DataGridViewRow)y;
foreach (var kvp in _sortingColumns)
{
string colName = kvp.Key;
ColumnSortInfo csi = kvp.Value;
string sVal1 = r1.Cells[colName].Value?.ToString().Trim()??"";
string sVal2 = r2.Cells[colName].Value?.ToString().Trim()??"";
int iCompareResult = 0;
switch (csi.MyValueConversion)
{
case ColumnSortInfo.ValueConversion.ToString:
iCompareResult = String.Compare(sVal1, sVal2);
break;
case ColumnSortInfo.ValueConversion.ToNumber:
double d1 = ColumnSortInfo.StringToDouble(sVal1);
double d2 = ColumnSortInfo.StringToDouble(sVal2);
iCompareResult = ((d1 == d2) ? 0 : ((d1 > d2) ? 1 : -1));
break;
case ColumnSortInfo.ValueConversion.ToDate:
DateTime dt1 = ColumnSortInfo.StringToDateTime(sVal1);
DateTime dt2 = ColumnSortInfo.StringToDateTime(sVal2);
iCompareResult = ((dt1 == dt2) ? 0 : ((dt1 > dt2) ? 1 : -1));
break;
default:
break;
}
iCompareResult = csi.SortOrderMultiplier * iCompareResult;
if (iCompareResult != 0) { return iCompareResult; }
}
return 0;
}
catch (Exception ex)
{
return 0;
}
}
}
Usage:
Dictionary<String, ColumnSortInfo> sortingColumns = new Dictionary<String, ColumnSortInfo>
{ {"policyNumber", new ColumnSortInfo(System.Windows.Forms.SortOrder.Ascending)},
{"MessageId", new ColumnSortInfo(System.Windows.Forms.SortOrder.Descending, ColumnSortInfo.ValueConversion.ToNumber)},
{"CreationDate", new ColumnSortInfo(System.Windows.Forms.SortOrder.Ascending, ColumnSortInfo.ValueConversion.ToDate)}};
CsUtils.SortOnMultipleColumns(dgv, sortingColumns);
This question already has answers here:
Sorting A ListView By Column
(15 answers)
Closed 8 years ago.
How to sort a listview control by a specific column number in WinForms .NET 2.0? e.g. I have a column called "Line Number" whose index is 1, and I want to sort my items in the listview box by that in ascending order.
There is example on MSDN ListView.ColumnClick article: very short and simple. Essentially, you write a ListViewItemComparer and use it each time you click a column:
class ListViewItemComparer : IComparer
{
private int col = 0;
public ListViewItemComparer(int column)
{
col = column;
}
public int Compare(object x, object y)
{
return String.Compare(((ListViewItem)x).SubItems[col].Text, ((ListViewItem)y).SubItems[col].Text);
}
}
class MyForm : Form
{
// private System.Windows.Forms.ListView listView1;
// ColumnClick event handler.
private void ColumnClick(object o, ColumnClickEventArgs e)
{
this.listView1.ListViewItemSorter = new ListViewItemComparer(e.Column);
}
}
I have used this column sorter in many Winform Projects:
private void listView1_ColumnClick(object sender,
System.Windows.Forms.ColumnClickEventArgs e)
{
ListView myListView = (ListView)sender;
// Determine if clicked column is already the column that is being sorted.
if ( e.Column == lvwColumnSorter.SortColumn )
{
// Reverse the current sort direction for this column.
if (lvwColumnSorter.Order == SortOrder.Ascending)
{
lvwColumnSorter.Order = SortOrder.Descending;
}
else
{
lvwColumnSorter.Order = SortOrder.Ascending;
}
}
else
{
// Set the column number that is to be sorted; default to ascending.
lvwColumnSorter.SortColumn = e.Column;
lvwColumnSorter.Order = SortOrder.Ascending;
}
// Perform the sort with these new sort options.
myListView.Sort();
}
Source: Click Here
I have a WPF DataGrid that is populated with data from DataSet. I have CanUserSortColumns set to true.
Is it possible to retain the sorting that the user specified when the grid is refreshed? I have it retaining the item that was selected using
object selectedItem = dgInvoiceHeads.SelectedItem;
before the refresh takes place and then placing
dgInvoiceHeads.SelectedItem = selectedItem;
after the refresh takes place.
But I can't seem to get it to retain the specified sort.
The following code was pulled from this forum post and it shows how to obtain the sort descriptions and column information and restore it.
List<DataGridColumn> GetColumnInfo(DataGrid dg) {
List<DataGridColumn> columnInfos = new List<DataGridColumn>();
foreach (var column in dg.Columns) {
columnInfos.Add(column);
}
return columnInfos;
}
List<SortDescription> GetSortInfo(DataGrid dg) {
List<SortDescription> sortInfos = new List<SortDescription>();
foreach (var sortDescription in dg.Items.SortDescriptions) {
sortInfos.Add(sortDescription);
}
return sortInfos;
}
void SetColumnInfo(DataGrid dg, List<DataGridColumn> columnInfos) {
columnInfos.Sort((c1, c2) => { return c1.DisplayIndex - c2.DisplayIndex; });
foreach (var columnInfo in columnInfos) {
var column = dg.Columns.FirstOrDefault(col => col.Header == columnInfo.Header);
if (column != null) {
column.SortDirection = columnInfo.SortDirection;
column.DisplayIndex = columnInfo.DisplayIndex;
column.Visibility = columnInfo.Visibility;
}
}
}
void SetSortInfo(DataGrid dg, List<SortDescription> sortInfos) {
dg.Items.SortDescriptions.Clear();
foreach (var sortInfo in sortInfos) {
dg.Items.SortDescriptions.Add(sortInfo);
}
}
Have you tried getting the collectionview for the dataset?
CollectionViewSource.GetDefaultView(yourDataSet).SortDescriptions
This will give you an array of the current sortdescriptions. You can then persist these, and the next time round apply them as follows
CollectionViewSource.GetDefaultView(yourDataSet).SortDescriptions.Add(...)
Hope it helps.
One of my colleagues came up with this. It seems to be working correctly. The only thing is I think the column headers need to be the same in the DataGrid as they are in the DB.
string sortHeader;
string prevSortHeader;
SortDescription sd;
private void dgInvoiceHeads_Sorting(object sender, DataGridSortingEventArgs e) {
sortHeader = e.Column.Header.ToString();
if (sortHeader == prevSortHeader) {
sd = new SortDescription(sortHeader, ListSortDirection.Descending);
}
else {
sd = new SortDescription(sortHeader, ListSortDirection.Ascending);
}
prevSortHeader = sortHeader;
}
HTH
private void testGrid_Sorting(object sender, DataGridSortingEventArgs e)
{
ListSortDirection direction = (e.Column.SortDirection != ListSortDirection.Ascending) ?
ListSortDirection.Ascending : ListSortDirection.Descending;
// You will get the current direction in direction
}
This is another solution
I'm using a Windows Forms DataGridView to display a generic list of MyObject objects.
First of all I wrap this collection into a BindingSource Collection, then:
dataGridView.DataSource = myBindingSource;
What I want to do is allow the user to sort the columns by clickin on the header of the column representing a concrete Property in MyObject.
I've read some articles that I should do sorting before binding. But it does not help me if I want to sort the columns in real time, being said when it's already binded.
The question is, what exactly do I need to do, so I could see the sorting arrows in DataGridView and I could sort every column ?
Complete code to sort the column of datagridview whose datasource is a generic 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;
}
}
I find it hard to believe the grid doesn't provide basic sorting out of the box, no code needed. After all, it is pretty silly to have to handle a header click event and call DataGridView.Sort indicating the column (determined by what was clicked, tracked by the grid) and the sort direction (determined by current sort state, tracked by the grid).
Why isn't there simply a SortMode or an AllowUserToSort property that does exactly the same thing by default?
I've bound my grid to a List and the properties I've mapped columns to are all basic types like string, int, DateTime and so on. All of which are IComparable. So why on earth should I need to write even one line of code? Especially considering that the documentation reads:
By default, users can sort the data in
a DataGridView control by clicking the
header of a text box column.
MSDN
That's the Framework 3.0 doc, and I'm targeting 3.5, but "other versions" all refer to versions of Visual Studio, not versions of the Framework. What on earth is going on here Microsoft?!?
A good Solution in this article "Presenting the SortableBindingList":
http://www.timvw.be/2007/02/22/presenting-the-sortablebindinglistt/
Here is a simpler solution to sort by column using Reflection and Linq. dataGridView1's DataSource is set to compareList which is declared as:
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; }
}
}
My Solution is this:
I work with myBindingSource at my own, I do sorting, grouping ..whatever in a separate thread.
Then I simply bind the result to the DataGridView.
myDataGridView.DataSource = bindingSource;
For this purpose I've setted all the columns to be sorted 'Programatically' (in designer)
Then I manually add the arrow (ASCENDING / DESCENDING)
by setting
cell.SortGlyphDirection = ... ;
in code behind.
If creating your own user control is preferable, you can make a custom sort method using the code below:
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;
}
You can then override the header click method to call your sort function:
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);
}
You may also want to take a look to this post where you can get two interesting links to implement a customized SortableBindingList:
Sort Datagridview columns when datasource binded to List(Of T)
see this artice
http://msdn.microsoft.com/en-us/library/0868ft3z.aspx
by reading it i saw this "This method sorts the contents of the DataGridView by comparing values in the specified column. By default, the sort operation will use the Compare method to compare pairs of cells in the column using the DataGridViewCell..::.Value property."
Best Regards,
iordan
Another option to solve the sorting issue with DataGridView when binding to List is, if you are not dealing with huge data set then probably you can attempt to convert the List to DataTable, and then bind the resulting DataTable to the BindingSource/DataGridView.
This would the need for a custom implementation of IComparer. In my case, I was dealing with a smaller list but there were more fields to display. So implementing IComparer meant writing too much boiler plate code.
Check this for terse way of converting the List to DataTable: https://stackoverflow.com/a/34062898/4534493
First of all I used System.Reflection;
then: write this method
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;
}
then call method : DataTable dt = ToDataTable(lst.ToList());
Universal sorting of DataGridView columns with DataSource
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();
}