Custom Sorting with N-Layer Design Classes and the GridView

Over the past couple of months I received a number of questions related to sorting with the classes from my N-Layer Design article series and the GridView. The good thing is: it isn't that hard; it's just that you need to know how to do it....


The code presented in this short article replaces the code for the ContactPersonManager.cs file in the BusinessLogic folder of the N-Layer Design application. If you haven't read the article series yet, please read them first, or otherwise you probably won't have a clue where this is all about. Simply copy it over the existing file and you're good to go. If you rather not copy over the entire file, then you need to look at two things: first, the GetList method in ContactPersonManager.cs has changed and now accepts a string parameter called sortExpression and secondly the class now has an additional region with a nested class called ContactPersonComparer that takes care of sorting.

The file TestSorting.aspx that also comes with this article's download is a very simple ASPX file with a simple GridView that demonstrates how to apply sorting. Take a look at the GridView and the connected ObjectDataSource first:

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
       DataKeyNames="Id" DataSourceID="ObjectDataSource1" AllowSorting="True">
    <asp:BoundField DataField="LastName" HeaderText="LastName" 
          SortExpression="LastName" />
    <asp:BoundField DataField="Id" HeaderText="Id" 
          InsertVisible="False" ReadOnly="True" SortExpression="Id" />
    <asp:BoundField DataField="FirstName" HeaderText="FirstName" 
          SortExpression="FirstName" />
    <asp:BoundField DataField="MiddleName" HeaderText="MiddleName" 
          SortExpression="MiddleName" />
    <asp:BoundField DataField="DateOfBirth" HeaderText="DateOfBirth" 
          SortExpression="DateOfBirth" />

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server" 
      SortParameterName="sortExpression" SelectMethod="GetList"

This all looks like a normal GridView and ObjectDataSource with one exception: the SortParameterName attribute on the ObjectDataSource:


This tells the ObjectDataSource to forward the selected SortExpression it receives from the GridView (when you click on the column headers) to the GetList method. I modified the GetList method slightly and it now accepts an additional sortExpression parameter:

public static ContactPersonList GetList(string sortExpression)
  ContactPersonList myList = ContactPersonDB.GetList();
  myList.Sort(new ContactPersonComparer(sortExpression));
  return myList;

Notice how the sortExpression parameter is sent to the constructor of ContactPersonComparer class. The ContactPersonComparer instance in turn is sent to the Sort method on the generics List which handles sorting for you. Whenever the Sort method needs to compare two objects, it'll ask the ContactPersonComparer object how to do it. That object does that with the Compare method you'll find in the nested ContactPersonComparer class inside the file ContactPersonManager.cs. The compare method looks like this:

public int Compare(ContactPerson a, ContactPerson b)
  int retVal = 0;
  switch (_sortColumn.ToLower())
    case "id":
      retVal = a.Id.CompareTo(b.Id);
    case "firstname":
      retVal = a.FirstName.CompareTo(b.FirstName);
    case "lastname":
      retVal = a.LastName.CompareTo(b.LastName);
    case "middlename":
      retVal = a.MiddleName.CompareTo(b.MiddleName);
    case "dateofbirth":
      retVal = a.DateOfBirth.CompareTo(b.DateOfBirth);
  int _reverseInt = 1;
  if ((_reverse))
    _reverseInt = -1;
  return (retVal * _reverseInt);

This method accepts two ContactPerson instances and then returns an int to indicate whether the left hand side or the right hand side is "greater" than the other. Basically, it leaves it to the individual property's CompareTo methods to determine which of the two is bigger. It uses the private _sortColumn variable to determine on what property to sort. _sortColumn is set in the class's constructor like this:

public ContactPersonComparer(string sortExpression)
  if (sortExpression.Length == 0)
    sortExpression = "Id desc";
  _reverse = sortExpression.ToLowerInvariant().EndsWith(" desc");
  if (_reverse)
    _sortColumn = sortExpression.Substring(0, sortExpression.Length - 5);
    _sortColumn = sortExpression;

The GridView keeps track of the sort column and the direction in which you want to sort (ascending or descending). You don't need to do anything to enable this two-way sorting mechanism. For example, the first time you click the Id header, the sortExpression will be "Id". This is sent to the ObjectDataSource which forwards it to the GetList method and eventually it ends up in the ContactPersonComparer's constructor. Since it simply contains "Id", nothing special is done with the value, and _sortColumn will simply be "Id". However, the second time you click the heading, the sortExpression will be "Id DESC" to indicate you want to sort in reverse order. In this case, _reverse is set to true and DESC is stripped from the sortExpression. The variables _sortColumn and _reverse are then used again in the Compare method to determine if one ContactPerson is "greater" than another.


Sorting is pretty easy with collections like the generics List class. You don't need to order items in the list manually or find out what item should be on top or at the bottom of the list. Instead, you need to create a class that implements IComparer<T> where T is the class you want to order on, like IComparer<ContactPerson>.

Once you have the class in place, you can copy the boilerplate code for the constructor as it's always the same code. The only thing you need to do is write case statements for the various properties of the sorted object. Note that you're not limited to simple properties. If you want, you could sort on things like the number of address in the Addresses collection if you wanted to.

Download Files

Download the modified files that demonstrate sorting

Where to Next?

Wonder where to go next? You can read existing comments below or you can post a comment yourself on this article .

Consider making a donation
Please consider making a donation using PayPal. Your donation helps me to pay the bills so I can keep running Imar.Spaanjaars.Com, providing fresh content as often as possible.

Feedback by Other Visitors of Imar.Spaanjaars.Com

On Thursday, June 14, 2007 9:42:08 PM Rob said:
How about filtering a list based off of a BO's property?  For example filter the list of Addresses where State is California, Alabama, and Alaska.

Also, in support, I bought your book "Instant results" from Amazon.  

I have to take a look to see if there is a List.Filter() example in there somewhere.

Thanks again.
On Thursday, June 14, 2007 9:59:46 PM Imar Spaanjaars said:
Hi Rob,

There is an example about filtering in the BugBase application (chapter 12 of my book)

It uses a custom Criteria object that is passed to te BLL where, in case of the BugBase the criteria are forwarded to the database. You can use that, or filter the list "client side" (compared to the database) in the GetList BLL method.


On Friday, June 15, 2007 6:33:25 PM SR said:
How to thank you ??
It's a pleasure to learn with your tutorials.
By the way Rob, I m ready to see how your code the filter function :-)

On Sunday, June 17, 2007 8:47:33 AM Imar Spaanjaars said:
Hi SR,

You're welcome....

Want to know how to thank me? Buy my book. That way, you can also see how filtering is done.... ;-)

On Wednesday, June 20, 2007 12:49:38 AM Rob said:
To SR and Imar,

So, I found it really challenging to try to filter generic lists using anonymous delgates (predicates).

My filters come to me as type NameValueCollection and look like:

Key                  Values
____               ________
"Owner_Ids"    "1,3,22,5,6"
"Airport_Ids"    "22,3,4,5,6"
"Terminal_ids"   "1,2,3,4,5,6,7,8"

I opted to just forward the filters to the DB.  The link below helped me in using these filters to search my tables.

Good luck and let me know if you have a better way.


On Monday, June 25, 2007 2:45:54 PM Leon said:
Imar, again many thanks. You tutorials are very clear, to me that is(beginning .NET and beginning ASP.NET)

- Leon
On Thursday, July 12, 2007 12:54:54 PM Hamza said:
hi Imar,
Thanks for this great article and for the 3-layer series too,i found exactly the response to questions i have look for for long time.Thanks again.
But still ,I have a question about how to compare two properties one of them is null,
In the above exemple,imagine that the date of birth is a nullable type (DateTime?) ,Person1 has a dateofbirth="12-10-2000" and Person2 has no dateofbirth ie:default to null since it's nullable,
if we try to sort the list of person by date of birth the comparer rise an error,Any idea will be of great help!
On Thursday, July 12, 2007 1:17:08 PM Imar Spaanjaars said:
Hi Hamza,

Inside the Compare method you can look at the object and/or its properties and return an apprpriate value if one of them is null.

Take a look here for a more detailed example:


On Saturday, July 28, 2007 8:38:57 AM Rahul jain said:
When sorting according to group number(its my Colum name of gridview), the numbers are sorted in the following order: 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21, … This could probably be solved by adding a zero in front of the numbers 1 – 9. Thus: 01, 02, 03, 04, 05, 06, 07, 08, 09, 10, 11, …

how to do this

Plzzzzzzzzz help me
On Saturday, July 28, 2007 3:48:41 PM Imar Spaanjaars said:
Hi Rahul,

Sounds like the underlying data type is not a number but a string, right? If that's the case, simply change it to a number and it should sort correctly.

If you can't change the type, simply prefix the values in the Compare method with leading zeros and then use the Compare method of the string to compare the values. Look into the PadLeft method of the String class for the leading zeros.

BTW: stressing me up by sending me personal messages on top of your comments here doesn't speed things up at all. On the contrary even. If I have the time for an answer here, I'll post it. If I don't, I won't.


On Monday, September 24, 2007 10:18:46 AM Arnie1000 said:
Hi Imar,

Just wondering if it is possible to use the database for sorting of columns that require no custom sorting, but are just straightforward like string or date. If the sortexpression is forwarded to the DAL layer, then you would not need the extra step of sorting in the BLL layer.
On Monday, September 24, 2007 10:39:52 AM Imar Spaanjaars said:
Hi Arnie1000,

Yes, you can do that. However, dynamic sorting in a SQL Server stored procedure is not a trivial thing to do. You may want to search Google for:

dynamic sort "sql server" "stored procedure"

to get some ideas.


On Monday, September 24, 2007 12:00:57 PM Arnie1000 said:
Yes I know, but it would be easy to do by building a dynamic query in the DAL.

Another issue was performance by the DAL requesting a full table after each new page request in a grid. When you need only <#pagesize> rows, returning thousands of rows becomes slow. I think it can be implemented by using the extra parameters StartRowIndexParameterName and MaximumRowsParameterName that the ObjectDataSource hands down to the BLL. Then the select query can be built up dynamically as well using the ROW_NUMBER construct in SQL Server 2005 to select only the row numbers needed. But it is also possible in SP's.

What are your thoughts on this ? Thanks!

On Monday, September 24, 2007 12:18:11 PM Imar Spaanjaars said:
Hi Arnie1000,

Yes, this is indeed a good solution. What we usually do is create an overload of GetList that accepts two ints for startIndex and pageSize. We forward these parameters to the stored procedure where we use them for database paging. The current version then calls the overload passing -1, -1 (or nullable ints) to indicate all records should be returned.

On Tuesday, October 16, 2007 5:44:30 PM Balaji said:
Sir Your Articles are exlent iam a begner of datnet tecnoligies i learned
lot of  ur articles thank you very much.i want some more code samples
please publish more code samples in N- tier Architechure
                                                                             Thanking you sir
On Friday, October 19, 2007 11:53:19 PM C.T. said:
how about to use LINQ to sort?
On Saturday, October 20, 2007 12:23:03 AM C.T. said:
can not know if this can work:

ContactPersonList myList = from p in ContactPersonDB.GetList()
   orderby sortExpression
   select p;
return mylist;
On Saturday, October 20, 2007 8:58:27 PM C.T. said:
I have made the GetList method by using LINQ to sort it, but can not post code here, please download it from
On Saturday, October 20, 2007 9:04:39 PM Imar Spaanjaars said:
Hi C.T.

You *can* post code here, but it's just not that easy as you can't post HTML brackets like < and > as the error message you got a few times tried to tell you ;-)

Anyway, interesting solution, especially your second solution for ordering. But wouldn't it be slightly easier if you skipped the ContactPersonManagerDB call and execute a LINQ query directly? I am not sure if in the current solution gains anything from using LINQ....

On Saturday, October 20, 2007 9:37:36 PM C.T. said:
Hello,really quick response there:) I am just a new one to the LINQ, and find LINQ can do some interesting things. for several hours and in ton of web pages today, i googled one  to make the second solution. not sure if LINQ can give us some help in such a scenario, maybe it is just a way to sort, now I just learn a little about LINQ can be used in objects, database and xml, need to learn more about LINQ how to use and how to use in right place. :) and here thank you for your great series articles "Building Layered Web Applications with Microsoft ASP.NET 2.0", got very more about *layered* web.
On Saturday, October 20, 2007 9:41:49 PM Imar Spaanjaars said:
You're welcome.

Yeah, LINQ is a lot of fun and allows you to do a lot of fancy things. Just for fun I wrote this a while back:

Good luck with your LINQ search....

On Saturday, October 20, 2007 10:14:18 PM C.T. said:
I had read the article 424, and also read the “ Storing Uploaded Files in a Database or in the File System with ASP.NET 2.0”,I m developing a site and is finding a good enough solution to distribute and manage images and files. the article 424 gives me a new idea to delete the orphan files, and the other article let me decide to store images to disk not to database.
about how to distribute images/files to different sub-domains of a web site, use code to distribute or use hardware to do, no idea yet.
On Sunday, November 4, 2007 8:32:33 PM Steph said:
Hello Imar!

First of all like many of your readers I have really enjoyed this series of posts about building layered applications that are very clear and well illustrated (and also show that we can get by without tableAdapters!).

I'm also grateful for this current post about sorting wich works very well.

However on the little of the detail side, I am struggling with what I would call the last touch of the "sorting". Indeed, if the sorting works well, the trouble is that when the user clicks for the first time on the column by which the results of the gridView is sorted by default, then "nothing" happens, especially when enabling sorting with callBacks. I mean I know that something happens as the collection of objects is sorted again by the sortExpression but for a user it does appear like a click that does nothing. Then the user would click again and this time the list is sorted by the reverse order.

So I am trying to find a way round this. What I used to do in datagrid in 1.x was intercept the sort command to amend the sortExpression based on a viewState variable to chase this very first sort accordingly.

Two disappointments there: not only sortExpression and sortDirection are readOnly properties but when using EnableSortingAndPagingCallbacks="True" I found that the "normal" sort events are not fired.

I was wondering whether you had given thoughts to that particular issue. Obviously we could lose the callBack and handle everything on the code behind but that would be a shame in a way.

Anyway thank you very much again if you have the magic answer. Your book is definitely on my wish-list ;-)

As some Scott is writing, "happy programming!".
On Friday, November 9, 2007 3:16:33 PM Steph said:
Hello again Imar!

I'am responding to my previous post as the solution was much simpler than I thought...

To get the GridView to "know and remember" that the collection of objects is already sorted the first time the results are displayed, one only needs to call the GridView.Sort() function in the page_load for instance, passing in parameters the initial sort expression and the initial sort direction. This way, when the user clicks on the initially sorted column when the results is first displayed, the gridview will sort directly in the reverse order (no need for an extra click from the user's point of view to get the reverse).

Also a possible workaround to have the SortEvent fired without losing the "ajax" behaviour is to use an Ajax UpdatePanel around the gridview and to set the EnableSortingAndPagingCallbacks to false.

Anyway I thought this might be of interest. Thank you again for the great posts.

On Friday, November 9, 2007 5:08:24 PM Imar Spaanjaars said:
Hi Steph,

Glad you got it working. I also prefer UpdatePanel controls over the default call back mechanism of the GridView.

Thanks for posting a follow up!

On Saturday, December 22, 2007 3:06:30 PM Nicolas said:
Hi Imar,

First i would like to thank you for the N-Layer Design Template that i used successfully as a basis for implementing a tool that dynamically generates online inquiry wizards based on database input of questions and possible answers, written in VB code.

Well, right now, i have just downloaded the files of this article, converted them to VB using the following online tool :

...and then added them to your template.

But i'm getting an error message in the ContactPersonManager.vb file at line 110 : Implements IComparer(Of ContactPerson)

The compiler error message (translated from french) says :

Class 'ContactPersonComparer' must implement 'Function Compare(x As Spaanjaars.ContactManager.BO.ContactPerson, y As Spaanjaars.ContactManager.BO.ContactPerson) As Integer' for the interface.

Where do you think the error comes from ?

This is not an urgent matter, and i know you are busy writing your new book, but in case you or one of the readers of this article have the answer, i would appreciate.


On Saturday, December 22, 2007 4:17:10 PM Imar Spaanjaars said:
Hi Nicolas,

You need to implement the interface's method explicitly, like this:

Public Function Compare(ByVal x As ContactPerson, ByVal y As ContactPerson) _
      As Integer Implements System.Collections.Generic.IComparer( _
      Of ContactPerson).Compare
End Function

Hope this helps,

On Saturday, December 22, 2007 4:41:12 PM Nicolas said:

That is correct, it works just fine now.


On Tuesday, March 11, 2008 6:13:20 AM mohammed abdullah said:
first of all great articles from great person
i start to learn 2.0 from your articles

if i have a lot of data and i need a sufficient paging i will turn on the paging feature of objectdatasource but if i did that your sort technique will not work except for the returning records for every page.
i found this article
demonstrate how to use sql sorting instead
if i am wrong please tell me because i found your code is nifty
thanks again Imar
On Tuesday, March 11, 2008 7:30:55 AM Imar Spaanjaars said:
Hi mohammed,

You're correct. When you do database paging, you also need to do database sorting, as otherwise you are only sorting the current page.

On Monday, April 7, 2008 5:10:52 AM umesh said:
some projects which could helps us at fresher level for development.
ideas that provide good knowledge about the concept being utilized in projects which u r sending.
On Monday, April 7, 2008 5:39:24 AM Imar Spaanjaars said:
Hi umesh,

Heuh? I have no idea what you're saying....

On Thursday, June 12, 2008 9:57:06 PM steve said:
this is a thing of beauty!
On Friday, June 13, 2008 7:39:47 PM Vladimir Kelman said:
Hi Imar!

In case select SQL is generated dynamically in DAL, it is easy to pass sort expression to SQL. In that case, what are advantages in sorting using List[T].Sort(IComparer(T)) over using SQL ORDER BY?
I think it might be easier to implement some fancy custom IComparer[T].Compare() in C#, than to do it in SQL, but are there other advantages?
What are disadvantages besides inability to use with pages?

Thank you!
P.S. I bought both your books :)
On Sunday, June 15, 2008 5:09:04 PM Imar Spaanjaars said:
Hi Vladimir,

Sorting in the database is a lot better and faster, especially if you combine it with database paging. So, if possible, go for database sorting. You should use .NET List sorting only for relatively small collections.


On Wednesday, June 18, 2008 5:58:27 PM Chaumette said:

I am having little trouble with the sorting direction.
The sort with desc works great, but my asc is not looking great.

(** most code is similar **)
I pass my sort statement to contactpersoncomparer:

ContactPersonComparer("CaseID desc ")


But, the ascending deos not sort the opposite of desc?

ContactPersonComparer("CaseID asc ")


Will I need to modify the Compare function?

On Wednesday, June 18, 2008 6:36:03 PM Chaumette said:
Apologies Imar, I re-read your article. I needed to write:


instead of:

ContactPersonComparer("CaseID asc ")

to handle ascending!!!!
On Monday, July 14, 2008 9:24:59 PM Vladimir Kelman said:
FYI, I found an interesting approach to sorting by writing an extension method and using Enumerable.OrderBy/Enumerable.OrderByDescending methods inside.
On Thursday, July 24, 2008 4:42:20 AM Daren said:

great article.

I was wondering however, What if I want to return an address list and a value indicating the total number of records in the table so that I can implement paging.

I need the totalPages so that I can calculate how many page numbers to display to the user, and I can't simply determine how many items exist in the list as I am using the SqlServer2005 row_over function to only get a subset of rows from my table (but I still need to know how many total rows there are, not just what is returned in the collection).
On Thursday, July 24, 2008 6:04:21 AM Imar Spaanjaars said:
Hi Daren,

There are two ways to do this:

1. ref params on the GetList. This way you can provide the count as an output parameter on the stored procedure and on the GetList method.

2. A separate method. The ObjectDataSource has a SelectCountMethod property that it calls on your BO to get the total number of records for the select method.

Hope this helps,

On Monday, August 18, 2008 11:11:59 AM Gufran said:
In my Gridview i m not getting the sorted data when i click first time on the gridview column but after that its working fine .. whats the problem for this?
On Thursday, August 21, 2008 8:34:53 AM Imar Spaanjaars said:
Hi Gufran,

Hard to tell wihout seeing your code. Can you post this on a form like


On Tuesday, December 16, 2008 3:58:09 PM Steve Bell said:
I am using your N-Layer framework for the project I am currently developing.  I have read through this sorting article a few times now, and I have tried to implement it in my project.  The only difference is, instead of using the objectdatasource control, I am binding in my code-behind pages.

In your example here your ODS has "SortParameterName="sortExpression"" - this is where I run into a snag.  In my project I am setting the datasource of a gridview = ContactPersonManager.GetList() rather than using the ODS.  I am doing this because I currently have 7 gridviews placed on my page that are all related - and I do not need them all binded on page load.

Can you offer any direction here???

On Tuesday, December 16, 2008 6:24:07 PM Imar Spaanjaars said:
Hi Steve,

You can handle the OnSorting event of the GridView:

It has an e parameter of type GridViewSortEventArgs which has a SortExpression property that you can feed directly to the GetList method. Then all you need to do is rebind the GridView like this:

// make the OnSorting generic for all GridViews. Sender *is* the GridView
GridView myGridView = sender as GridView;
myGridView.DataSource = MyBusinessObject.GetList(e.SortExpression);

Untested code (typed directly in this comment) but it should work or at least put you on the right track.

Does that help?

On Tuesday, March 24, 2009 4:11:40 PM Steve Bell said:

I have the sorting working -- but only in 1 direction - ascending.

I have read some of the posts above with regards to this (like sorting the gridview on page load and such..) but nothing seems to work.

If, in my example, I have 3 columns..


I want to have it so that you click Last Name and it sorts ascending, you click again, and it sorts descending..

What am I missing here??

On Tuesday, March 24, 2009 4:14:59 PM Imar Spaanjaars said:
Hi Steve,

What control are you using? The GridView should support this out of the box and extend the sortExpression with "desc" to do descending sorting.

On Tuesday, March 24, 2009 4:18:37 PM Steve Bell said:
Wow - Talk about quick reply time!

I am using the gridview.  

Let me clarify, I am not sorting the gridview on page load, but I tried it and it did not produce the desired result.  Here is my tag definition.

     AllowSorting="true" ]

Thanks again.
On Tuesday, March 24, 2009 4:30:16 PM Imar Spaanjaars said:
Hi Steve,

You don't have to do it in Page_Load. The GridView and ObjectDataSource should be able to work this out just fine. Few things to check:

1. Make sure you have a sortExpression on each relevant column.

2. Make sure ViewState is enabled (the GridView needs to keep track of the SortDirection; not sure if this is stored in ViewState or ControlState)

3. Make sure you set up a SortParameterName on the ObjectDataSource.

4, Make sure your ODS method accepts that sort expression.

Take another look at the code that comes with this article; everything you need is demonstrated there.


On Thursday, March 26, 2009 2:00:19 PM Sanjay said:
Hi Imar ,

Thanks for the article . I tried to use same logic for  gridview with template items and it did not work. When I use it without templates, It works properly.

Please let me know why it will not work on gridview with template.(It was taking sort expression as empty string).
On Thursday, March 26, 2009 2:09:26 PM Imar Spaanjaars said:
Hi Sanjay,

You need to set the SortExpression on the asp:TemplateField

Hope this helps,

On Saturday, January 9, 2010 11:49:05 PM Hadi said:
Hello Imar.

Thanks for your very helpful article.
Would you please say how to add check box featrue?
I have added the checkBox to page and added a relared field in database with bit dataype. what else??
is it like the dropdownlist?? have to create Enum??

Thanks for your help
On Sunday, January 10, 2010 10:03:51 AM Imar Spaanjaars said:
Hi Hadi,

A checkbox is simply a bool property on your business object and a bit in the database. You assign it like this:

myObject.SomeBoolProperty = myCheckbox.Checked;

and read it like this:

myCheckbox.Checked = myObject.SomeBoolProperty;

You can treat it pretty much like all other properties in your business ibject.


On Tuesday, February 2, 2010 10:55:15 AM Jagdeep Mankotia said:
I need something link with database using LINQ.

On Tuesday, February 2, 2010 10:56:42 AM Imar Spaanjaars said:
Hi Jagdeep,

You need something link? That doesn't make a whole lot of sense to me....


Talk Back! Comment on Imar.Spaanjaars.Com

I am interested in what you have to say about this article. Feel free to post any comments, remarks or questions you may have about this article. The Talk Back feature is not meant for technical questions that are not directly related to this article. So, a post like "Hey, can you tell me how I can upload files to a MySQL database in PHP?" is likely to be removed. Also spam and unrealistic job offers will be deleted immediately.

When you post a comment, you have to provide your name and the comment. Your e-mail address is optional and you only need to provide it if you want me to contact you. It will not be displayed along with your comment. I got sick and tired of the comment spam I was receiving, so I have protected this page with a simple calculation exercise. This means that if you want to leave a comment, you'll need to complete the calculation before you hit the Post Comment button.

If you want to object to a comment made by another visitor, be sure to contact me and I'll look into it ASAP. Don't forget to mention the page link, or the QuickDocId of the document.

For more information about the Talk Back feature, check out this news item.