Create a WinForms application. Let's start with a form with a button and a DataGridView.
The button will create a List of Employees (see the previous post) and populate it. After that, we'll bind the list to our datagrid:
private void btnLoad_Click(object sender, EventArgs e)
{
List<Employee> employees = new List<Employee>();
//add employees to the list
employees.Add(new Employee("1", "Elijah", "Baley", 5000.50));
employees.Add(new Employee("2", "Daneel", "Olivaw", 3650.80));
employees.Add(new Employee("3", "Hari", "Seldon", 8450.25));
//bind the datagrid
dgvEmployees.DataSource = employees;
}
If you run the app and click the button you will get something like this:
Let's get to the Sort part. Remember we created some Comparer classes for our Employee Class. However, those comparers will work only to sort in an Ascending order. Let's create more to sort in Descending order. The only change here is that we will compare the objects backwards:
In ascending order we compare like this:
x.property.CompareTo(y.property)
In descending order we just use:
y.property.CompareTo(x.property)
So here you have all the comparers we will use:
//ASCENDING
public class EmployeeComparerFirstName : IComparer<Employee>
{
public int Compare(Employee x, Employee y)
{
return x.FirstName.CompareTo(y.FirstName);
}
}
public class EmployeeComparerLastName : IComparer<Employee>
{
public int Compare(Employee x, Employee y)
{
return x.LastName.CompareTo(y.LastName);
}
}
public class EmployeeComparerSalary : IComparer<Employee>
{
public int Compare(Employee x, Employee y)
{
return x.Salary.CompareTo(y.Salary);
}
}
//DESCENDING
public class EmployeeComparerEmployeeIdDSC : IComparer<Employee>
{
public int Compare(Employee x, Employee y)
{
return y.EmployeeId.CompareTo(x.EmployeeId);
}
}
public class EmployeeComparerFirstNameDSC : IComparer<Employee>
{
public int Compare(Employee x, Employee y)
{
return y.FirstName.CompareTo(x.FirstName);
}
}
public class EmployeeComparerLastNameDSC : IComparer<Employee>
{
public int Compare(Employee x, Employee y)
{
return y.LastName.CompareTo(x.LastName);
}
}
public class EmployeeComparerSalaryDSC : IComparer<Employee>
{
public int Compare(Employee x, Employee y)
{
return y.Salary.CompareTo(x.Salary);
}
}
You may wonder why we have a desceding comparer for EmployeeId property but not the ascending one. That's because the ascending comparer for that property is the default comparer in our Employee Class when it implements IComparable. That's in the CompareTo method (see part I).
We can use any of this comparers to sort the columns of our datagrid, the problem is how to know which comparer should we use. To decide that we'll create a method that will return the right IComparer we need, based on the name of the property that column is bound to and the sort order.
private IComparer<Employee> getEmployeeComparer(String propertyName, System.Windows.Forms.SortOrder sortDirection)
{
switch (propertyName)
{
case "FirstName":
if (sortDirection == SortOrder.Ascending)
return new EmployeeComparerFirstName();
else
return new EmployeeComparerFirstNameDSC();
case "LastName":
if (sortDirection == SortOrder.Ascending)
return new EmployeeComparerLastName();
else
return new EmployeeComparerLastNameDSC();
case "Salary":
if (sortDirection == SortOrder.Ascending)
return new EmployeeComparerSalary();
else
return new EmployeeComparerSalaryDSC();
default:
if (sortDirection == SortOrder.Ascending)
return null;
else
return new EmployeeComparerEmployeeIdDSC();
}
}
You can see that if the property name is not found, the list will be sorted by EmployeeId. Additionally, if the sort order is ascending the method will return null. That's because if the Sort method of List
You can write this method in the Employee class making it static or in a different class with all the other comparers. For now let's just put it in the code of our Form.
The next step is to handle the event ColumnHeaderMouseDoubleClick of the DataGridView, so when the user double-clicks the header of a column we will sort that column.
private void dgvEmployees_ColumnHeaderMouseDoubleClick(object sender, DataGridViewCellMouseEventArgs e)
{
//get list from datasource
List<Employee> employees = (List<Employee>)dgvEmployees.DataSource;
//set sort order
SortOrder sortOrder = (dgvEmployees.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending);
//sort list
employees.Sort(getEmployeeComparer(dgvEmployees.Columns[e.ColumnIndex].DataPropertyName, sortOrder));
//remove sort glyph from any other column
unmarkSortedColumns(dgvEmployees);
//set sort glyph for current column
dgvEmployees.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = sortOrder;
//refresh grid to see the changes
dgvEmployees.Refresh();
}
Here is the big thing.
1) We cast the data source of the DataGridView to an Employee List.
List<Employee> employees = (List<Employee>)dgvEmployees.DataSource;
2) We decide the new sort order. If the column is already sorted with ascending order then we will sorted descending, else if the sort order is descending or the column is not sorted at all we will use ascending order.
SortOrder sortOrder = (dgvEmployees.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending);
3) Now we sort the List (notice that we sort the List and not the DataGridView) using our GetEmployeeComparer method to get the right comparer we need. To do this we pass the sort order and the name of the property. To get the name of the property just look at the DataPropertyName attribute of the current column.
employees.Sort(getEmployeeComparer(dgvEmployees.Columns[e.ColumnIndex].DataPropertyName, sortOrder));
4) An additional step is to remove the sorted glyph to any other column that was previously sorted. To do this we write the function unmarkSortedColumns passsing the our DataGridView, this way we can reuse this method when sorting other grids.
private void unmarkSortedColumns(DataGridView dataGrid)
{
foreach (DataGridViewColumn col in dataGrid.Columns)
{
if (col.HeaderCell.SortGlyphDirection != SortOrder.None)
col.HeaderCell.SortGlyphDirection = SortOrder.None;
}
}
5) After that we set the sort glyph to the current column we are sorting. Remember that the SortGlyphDirection property or the HeaderCell of our column is the one that sets that arrow icon in the column header that indicates the column is sorted.
dgvEmployees.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection = sortOrder;
6) And the last thing, but not less important, is to refresh the DataGridView. Otherwise, how would it know that the List has been order?
dgvEmployees.Refresh();
And that's it for now, your DataGrid will be sorted by the column you double-click:
In conclusion: you can implement IComparable and IComparer to compare you business objects by some property or based on an algorithm. A practical usage of these interfaces is to sort a list of business objects and sort the columns of a DataGridView when it is bound to a list of business objects.
That's it for this serie of posts, however expect a surprise about topic. soon.
See Part I and Part II
No comments:
Post a Comment