datagridview sortable binding list how can I have a secondary sort?

Posted on 2013-08-27
Medium Priority
Last Modified: 2013-08-31
I implemented a sortable binding list which can be found here: http://www.martinwilley.com/net/code/forms/sortablebindinglist.html.  

I bind the object to my grid like the code below.  This works well. I have a date column, which I would like to be the secondary sort though.  So if you sort by title, I want to sort by title then date.  How can I do this?

SortableBindingList<UnassociatedTradeView> filtered = new SortableBindingList<UnassociatedTradeView>(listBinding.Where(obj => !associatedTradeIds.Contains(obj.orderNumber)).ToList());
dataGridUnassociatedTrades.DataSource = filtered;

foreach (DataGridViewColumn column in dataGridUnassociatedTrades.Columns)
dataGridUnassociatedTrades.Columns[column.Name].SortMode = DataGridViewColumnSortMode.Automatic;
Question by:jackjohnson44
  • 2

Author Comment

ID: 39445113
Please do not post generic links!
LVL 40

Accepted Solution

Jacques Bourgeois (James Burger) earned 2000 total points
ID: 39445872
You need to create a class that implements System.Collections.IComparer, and use an instance of that class as a parameter to the Sort method of the grid. Here is a real life example. It's in VB, but the technique is the same in C#.
	Private Class DocumentationComparer
		Implements System.Collections.IComparer

		'Used to sort multiple columns together in the FormDocumentation grid

		Public Sub New(columnToSort As Integer, direction As SortOrder)
			_columnToSort = columnToSort
			_direction = direction
		End Sub

		Public Function Compare(x As Object, y As Object) As Integer Implements System.Collections.IComparer.Compare

			Dim result As Integer
			Dim rowX As DataGridViewRow = DirectCast(x, DataGridViewRow)
			Dim rowY As DataGridViewRow = DirectCast(y, DataGridViewRow)

			Select Case _columnToSort

				Case FormDocumentation.ColumnsID.SubjectID
					'Subject is always sorted in parallel with the Description
					If CInt(rowX.Cells(FormDocumentation.ColumnsID.SubjectID).Value) = CInt(rowY.Cells(FormDocumentation.ColumnsID.SubjectID).Value) Then
						'We sub-sort on the Description
						result = String.Compare(CStr(rowX.Cells(FormDocumentation.ColumnsID.Description).Value), CStr(rowY.Cells(FormDocumentation.ColumnsID.Description).Value))
						'We sort on the Subject
						result = String.Compare(CStr(rowX.Cells(FormDocumentation.ColumnsID.SubjectID).FormattedValue), CStr(rowY.Cells(FormDocumentation.ColumnsID.SubjectID).FormattedValue))
					End If

				Case FormDocumentation.ColumnsID.SourceID
					'Subsort the issue and page for the Source
					If CInt(rowX.Cells(FormDocumentation.ColumnsID.SourceID).Value) = CInt(rowY.Cells(FormDocumentation.ColumnsID.SourceID).Value) Then
						'We sub-sort on the Issue
						If CStr(rowX.Cells(FormDocumentation.ColumnsID.IssueUrl).Value) = CStr(rowY.Cells(FormDocumentation.ColumnsID.IssueUrl).Value) Then
							'We sub-sort on the Page
							result = CInt(rowX.Cells(FormDocumentation.ColumnsID.Page).Value).CompareTo(CInt(rowY.Cells(FormDocumentation.ColumnsID.Page).Value))
							'We sort on the Issue
							result = String.Compare(CStr(rowX.Cells(FormDocumentation.ColumnsID.IssueUrl).Value), CStr(rowY.Cells(FormDocumentation.ColumnsID.IssueUrl).Value), StringComparison.CurrentCulture)
						End If
						'We sort on the Source
						result = String.Compare(CStr(rowX.Cells(FormDocumentation.ColumnsID.SourceID).FormattedValue), CStr(rowY.Cells(FormDocumentation.ColumnsID.SourceID).FormattedValue), StringComparison.CurrentCulture)
					End If

				Case Else
					'The other columns are sorted on their content
					result = String.Compare(CStr(rowX.Cells(_columnToSort).FormattedValue), CStr(rowY.Cells(_columnToSort).FormattedValue), StringComparison.CurrentCulture)

			End Select

			If _direction = SortOrder.Ascending Then
				Return result
				'We need to inverse the result
				Return -result
			End If

		End Function

	End Class

Open in new window

Note that you can make it a lot simpler, I will come back to that at the end of my post.

But in my case, but in my case, I needed to be able to sort on different columns and did not want to have to create a class for each sort. I thus pass parameters to the constructor (New method in VB) telling the class on which column to sort and in which order, and coded so that the same class came be used for different columns.

The class does not perform the sort. It simply tells the grid how to go about comparing 2 rows. This is done in the Compare method, that will be called repetitively by the grid sorting mechanism with sets of 2 rows. All you have to do is to return a value less than zero if the first row should come before the second one, zero if they are seen as equal, and greater than zero if the first if the second one should come first.

The Select Case makes a difference between sorting on a SubjectID column, a SourceID column, and all the other columns that do not have a special sort. The FormDocumentation.ColumnsID enumeration that defines the index of the columns in the grid, so I am basically working with the column indexes here to retrieve the values to sort on. You could use the column names, but that method can be called hundreds of times to sort a big grid, and working with the indexes is a lot faster.

My code works with a grid that displays references in a collection of magazines. The second Case, on FormDocumentation.ColumnsID.SourceID is a multi-column sort. When the user wants to sort by magazine (SourceID), and when the method is sent 2 rows with the same magazine name, it sorts first by magazine name (SourceID), then the Issue (IssueID), and then by the page number. Integers and Strings already have Compare methods that work exactly on the same concept as the one we are defining here, so I use them to return the appropriate "less than zero / zero / greater than zero" values.

When you need to sort the grid, all you have to do is the following:

YourGrid.Sort(New DocumentationComparer(<Index of the column I want to sort>,<SortOrder>))

If you need only one type of sort for the grid, then everything can be a lot simpler. You do not need to create a constructor that receives parameters and do not need the Case in the Compare method, only your single algorithm. The method sort can then be called simply by YourGrid.Sort(New <YourComparerClass>)

Author Closing Comment

ID: 39455481
Thanks, that was detailed!

Featured Post

Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

Join & Write a Comment

A long time ago (May 2011), I have written an article showing you how to create a DLL using Visual Studio 2005 to be hosted in SQL Server 2005. That was valid at that time and it is still valid if you are still using these versions. You can still re…
This article shows how to deploy dynamic backgrounds to computers depending on the aspect ratio of display
Watch the video to know how one can repair corrupt Exchange OST file effortlessly and convert OST emails to MS Outlook PST file format by using Kernel for OST to PST converter tool. It can convert OST to MSG, MBOX, EML to access them. It can migrate…
Through the video, you can check the migration process of Outlook PST file to PDF. Kernel for Outlook to PDF tool can convert Outlook emails with all attributes like Subject, To, From, Cc, Bcc and other folders such as Inbox, Outbox, Sent Items, Jun…

627 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question