Alphabetical Paging on a SharePoint List View
One of the requirements I have had to work on lately is reproducing elements of a Domino UI in sharepoint. That happens to include a long list of sites with links at the top providing the ability to view sites starting with a particular letter.
This of course is quite different from the standard SharePoint paging/filtering/grouping, which is based on page numbers and full text values.
The first approach I tried was to add a filter provider web part implementing ITransformableFilterValues as documented by Ton Stegeman - MOSS 2007 Filter webparts part 1 - create your own provider and consumer .
Unfortunately, the out of box list view web part doesn’t provide particularly good support for filter providers. Sending the connection from the filter provider fails with the error “AvailableLinks did not implement the GetInitEventArgs method”. Setting up the connection the other way works, but is still quite limited. You can’t filter on a url field, the filter operator is always equals, and you can only filter on a column that is visible in the current view.
The first two constraints mean that we will need to create an additional column “FirstLetter” that is calculated based on the url title.
The third constraint means that we will need to use query string filters instead of a connected web part. Query string filters do have a few limitations, in particular that they apply to every list on the page. However, this will not be a problem in this case as the page will only contain one list view.
The filter portion is easy enough. In the web part render method, create a series of links in the format
<span style="color:#a65700;"><</span><span style="font-weight:bold;color:#800000;">a</span><span style="color:#274796;"> </span><span style="color:#074726;">href</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"?FilterField1=FirstLetter&FilterValue1=A"</span><span style="color:#a65700;">></span>A<span style="color:#a65700;"></</span><span style="font-weight:bold;color:#800000;">a</span><span style="color:#a65700;">></span>
AlphabetFilterWebPart.cs Render Method
<span style="font-weight:bold;color:#800000;">protected</span> <span style="font-weight:bold;color:#800000;">override</span> <span style="font-weight:bold;color:#800000;">void</span> Render<span style="color:#808030;">(</span>HtmlTextWriter writer<span style="color:#808030;">)</span>
<span style="color:#800080;">{</span>
<span style="font-weight:bold;color:#800000;">try</span>
<span style="color:#800080;">{</span>
writer<span style="color:#808030;">.</span>Write<span style="color:#808030;">(</span><span style="font-weight:bold;color:#800000;">string</span><span style="color:#808030;">.</span>Format<span style="color:#808030;">(</span><span style="color:#800000;">"</span><span style="color:#0000e6;"><a>{1}</a> </span><span style="color:#800000;">"</span><span style="color:#808030;">,</span> <span style="color:#800000;">"</span><span style="color:#0000e6;">0</span><span style="color:#800000;">"</span><span style="color:#808030;">,</span> <span style="color:#800000;">"</span><span style="color:#0000e6;">All</span><span style="color:#800000;">"</span><span style="color:#808030;">)</span><span style="color:#808030;">)</span><span style="color:#800080;">;</span>
writer<span style="color:#808030;">.</span>Write<span style="color:#808030;">(</span><span style="font-weight:bold;color:#800000;">string</span><span style="color:#808030;">.</span>Format<span style="color:#808030;">(</span><span style="color:#800000;">"</span><span style="color:#0000e6;"><a>{1}</a> </span><span style="color:#800000;">"</span><span style="color:#808030;">,</span> <span style="color:#800000;">"</span><span style="color:#0000e6;">0</span><span style="color:#800000;">"</span><span style="color:#808030;">,</span> <span style="color:#800000;">"</span><span style="color:#0000e6;">0-9</span><span style="color:#800000;">"</span><span style="color:#808030;">)</span><span style="color:#808030;">)</span><span style="color:#800080;">;</span>
<span style="font-weight:bold;color:#800000;">foreach</span> <span style="color:#808030;">(</span><span style="font-weight:bold;color:#800000;">char</span> c <span style="font-weight:bold;color:#800000;">in</span> <span style="color:#800000;">"</span><span style="color:#0000e6;">ABCDEFGHIJKLMNOPQRSTUVWXYZ</span><span style="color:#800000;">"</span><span style="color:#808030;">.</span>ToCharArray<span style="color:#808030;">(</span><span style="color:#808030;">)</span><span style="color:#808030;">)</span>
<span style="color:#800080;">{</span>
writer<span style="color:#808030;">.</span>Write<span style="color:#808030;">(</span><span style="font-weight:bold;color:#800000;">string</span><span style="color:#808030;">.</span>Format<span style="color:#808030;">(</span><span style="color:#800000;">"</span><span style="color:#0000e6;"><a>{1}</a> </span><span style="color:#800000;">"</span><span style="color:#808030;">,</span> c<span style="color:#808030;">,</span> c<span style="color:#808030;">)</span><span style="color:#808030;">)</span><span style="color:#800080;">;</span>
<span style="color:#800080;">}</span>
<span style="color:#800080;">}</span>
<span style="font-weight:bold;color:#800000;">catch</span> <span style="color:#808030;">(</span>Exception ex<span style="color:#808030;">)</span>
<span style="color:#800080;">{</span>
writer<span style="color:#808030;">.</span>Write<span style="color:#808030;">(</span>ex<span style="color:#808030;">.</span>Message <span style="color:#808030;">+</span> <span style="color:#800000;">"</span><span style="color:#0000e6;"> </span><span style="color:#800000;">"</span> <span style="color:#808030;">+</span> ex<span style="color:#808030;">.</span>StackTrace<span style="color:#808030;">)</span><span style="color:#800080;">;</span>
<span style="color:#800080;">}</span>
<span style="color:#800080;">}</span>
Populating the FirstLetter column is a little more complex. A calculated field would be ideal, but that won’t work for a links list as the URL is a complex field type and not supported in a calculated field formula.
The easiest way to handle this is with an event handler. When the link is added or updated, use .net code to update the FirstLetter field.
All the bits not shown are the defaults created by the WSPBuilder templates.
**Elements.xml **
<span style="color:#004a43;"><?</span><span style="color:#004a43;">xml</span> <span style="color:#004a43;">version</span><span style="color:#808030;">=</span><span style="color:#008c00;">"1.0"</span> <span style="color:#004a43;">encoding</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"utf-8"</span> <span style="color:#004a43;">?></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Elements</span> <span style="color:#666616;">xmlns</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"</span><span style="color:#666616;">http</span><span style="color:#800080;">:</span><span style="font-weight:bold;color:#800000;">//</span><span style="color:#5555dd;">schemas.microsoft.com</span><span style="color:#40015a;">/sharepoint/</span><span style="color:#0000e6;">"</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Module</span> <span style="color:#274796;">Name</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"</span><span style="color:#0000e6;">WebPartPopulation</span><span style="color:#0000e6;">"</span> <span style="color:#274796;">Url</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"</span><span style="color:#0000e6;">_catalogs/wp</span><span style="color:#0000e6;">"</span> <span style="color:#274796;">RootWebOnly</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"</span><span style="color:#0000e6;">TRUE</span><span style="color:#0000e6;">"</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">File</span> <span style="color:#274796;">Url</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"</span><span style="color:#0000e6;">AlphabetFilterWebPart.webpart</span><span style="color:#0000e6;">"</span> <span style="color:#274796;">Type</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"</span><span style="color:#0000e6;">GhostableInLibrary</span><span style="color:#0000e6;">"</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Property</span> <span style="color:#274796;">Name</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"</span><span style="color:#0000e6;">Group</span><span style="color:#0000e6;">"</span> <span style="color:#274796;">Value</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"</span><span style="color:#0000e6;">MyGroup</span><span style="color:#0000e6;">"</span><span style="color:#a65700;">></span><span style="color:#a65700;"></</span><span style="color:#5f5035;">Property</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Property</span> <span style="color:#274796;">Name</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"</span><span style="color:#0000e6;">QuickAddGroups</span><span style="color:#0000e6;">"</span> <span style="color:#274796;">Value</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"</span><span style="color:#0000e6;">MyGroup</span><span style="color:#0000e6;">"</span> <span style="color:#a65700;">/></span>
<span style="color:#a65700;"></</span><span style="color:#5f5035;">File</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"></</span><span style="color:#5f5035;">Module</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Receivers</span> <span style="color:#274796;">ListTemplateId</span><span style="color:#808030;">=</span><span style="color:#0000e6;">"</span><span style="color:#0000e6;">103</span><span style="color:#0000e6;">"</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Receiver</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Name</span><span style="color:#a65700;">></span>AddFirstLetter<span style="color:#a65700;"></</span><span style="color:#5f5035;">Name</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Type</span><span style="color:#a65700;">></span>ItemAdded<span style="color:#a65700;"></</span><span style="color:#5f5035;">Type</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">SequenceNumber</span><span style="color:#a65700;">></span>10000<span style="color:#a65700;"></</span><span style="color:#5f5035;">SequenceNumber</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Assembly</span><span style="color:#a65700;">></span>AlphabetFilter, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1f633ef6b7fa5501<span style="color:#a65700;"></</span><span style="color:#5f5035;">Assembly</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Class</span><span style="color:#a65700;">></span>AlphabetFilter.FirstLetterEventHandler<span style="color:#a65700;"></</span><span style="color:#5f5035;">Class</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Data</span><span style="color:#a65700;">></span><span style="color:#a65700;"></</span><span style="color:#5f5035;">Data</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Filter</span><span style="color:#a65700;">></span><span style="color:#a65700;"></</span><span style="color:#5f5035;">Filter</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"></</span><span style="color:#5f5035;">Receiver</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Receiver</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Name</span><span style="color:#a65700;">></span>UpdateFirstLetter<span style="color:#a65700;"></</span><span style="color:#5f5035;">Name</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Type</span><span style="color:#a65700;">></span>ItemUpdated<span style="color:#a65700;"></</span><span style="color:#5f5035;">Type</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">SequenceNumber</span><span style="color:#a65700;">></span>10000<span style="color:#a65700;"></</span><span style="color:#5f5035;">SequenceNumber</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Assembly</span><span style="color:#a65700;">></span>AlphabetFilter, Version=1.0.0.0, Culture=neutral, PublicKeyToken=1f633ef6b7fa5501<span style="color:#a65700;"></</span><span style="color:#5f5035;">Assembly</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Class</span><span style="color:#a65700;">></span>AlphabetFilter.FirstLetterEventHandler<span style="color:#a65700;"></</span><span style="color:#5f5035;">Class</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Data</span><span style="color:#a65700;">></span><span style="color:#a65700;"></</span><span style="color:#5f5035;">Data</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"><</span><span style="color:#5f5035;">Filter</span><span style="color:#a65700;">></span><span style="color:#a65700;"></</span><span style="color:#5f5035;">Filter</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"></</span><span style="color:#5f5035;">Receiver</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"></</span><span style="color:#5f5035;">Receivers</span><span style="color:#a65700;">></span>
<span style="color:#a65700;"></</span><span style="color:#5f5035;">Elements</span><span style="color:#a65700;">></span>
**FirstLetterEventHandler.cs **
<span style="font-weight:bold;color:#800000;">using</span> System<span style="color:#800080;">;</span>
<span style="font-weight:bold;color:#800000;">using</span> System<span style="color:#808030;">.</span>Collections<span style="color:#808030;">.</span>Generic<span style="color:#800080;">;</span>
<span style="font-weight:bold;color:#800000;">using</span> System<span style="color:#808030;">.</span>Text<span style="color:#800080;">;</span>
<span style="font-weight:bold;color:#800000;">using</span> Microsoft<span style="color:#808030;">.</span>SharePoint<span style="color:#800080;">;</span>
<span style="font-weight:bold;color:#800000;">using</span> System<span style="color:#808030;">.</span>Text<span style="color:#808030;">.</span>RegularExpressions<span style="color:#800080;">;</span>
<span style="font-weight:bold;color:#800000;">namespace</span> AlphabetFilter
<span style="color:#800080;">{</span>
<span style="font-weight:bold;color:#800000;">public</span> <span style="font-weight:bold;color:#800000;">class</span> FirstLetterEventHandler<span style="color:#808030;">:</span>SPItemEventReceiver
<span style="color:#800080;">{</span>
<span style="font-weight:bold;color:#800000;">private</span> <span style="font-weight:bold;color:#800000;">void</span> SetFirstLetter<span style="color:#808030;">(</span>SPListItem item<span style="color:#808030;">)</span>
<span style="color:#800080;">{</span>
<span style="font-weight:bold;color:#800000;">if</span> <span style="color:#808030;">(</span><span style="color:#808030;">!</span>item<span style="color:#808030;">.</span>Fields<span style="color:#808030;">.</span>ContainsField<span style="color:#808030;">(</span><span style="color:#800000;">"</span><span style="color:#0000e6;">URL</span><span style="color:#800000;">"</span><span style="color:#808030;">)</span><span style="color:#808030;">)</span> <span style="font-weight:bold;color:#800000;">return</span><span style="color:#800080;">;</span>
<span style="font-weight:bold;color:#800000;">if</span> <span style="color:#808030;">(</span><span style="color:#808030;">!</span>item<span style="color:#808030;">.</span>Fields<span style="color:#808030;">.</span>ContainsField<span style="color:#808030;">(</span><span style="color:#800000;">"</span><span style="color:#0000e6;">FirstLetter</span><span style="color:#800000;">"</span><span style="color:#808030;">)</span><span style="color:#808030;">)</span> <span style="font-weight:bold;color:#800000;">return</span><span style="color:#800080;">;</span>
SPFieldUrlValue urlvalue <span style="color:#808030;">=</span> <span style="font-weight:bold;color:#800000;">new</span> SPFieldUrlValue<span style="color:#808030;">(</span><span style="color:#800000;">"</span><span style="color:#800000;">"</span><span style="color:#808030;">+</span>item<span style="color:#808030;">[</span><span style="color:#800000;">"</span><span style="color:#0000e6;">URL</span><span style="color:#800000;">"</span><span style="color:#808030;">]</span><span style="color:#808030;">)</span><span style="color:#800080;">;</span>
<span style="font-weight:bold;color:#800000;">string</span> firstlettervalue <span style="color:#808030;">=</span> <span style="color:#800000;">"</span><span style="color:#800000;">"</span> <span style="color:#808030;">+</span> item<span style="color:#808030;">[</span><span style="color:#800000;">"</span><span style="color:#0000e6;">FirstLetter</span><span style="color:#800000;">"</span><span style="color:#808030;">]</span><span style="color:#800080;">;</span>
<span style="font-weight:bold;color:#800000;">string</span> linktitle <span style="color:#808030;">=</span> urlvalue<span style="color:#808030;">.</span>Description<span style="color:#800080;">;</span>
<span style="font-weight:bold;color:#800000;">if</span> <span style="color:#808030;">(</span>
<span style="color:#808030;">!</span><span style="font-weight:bold;color:#800000;">string</span><span style="color:#808030;">.</span>IsNullOrEmpty<span style="color:#808030;">(</span>linktitle<span style="color:#808030;">)</span>
&& <span style="color:#808030;">(</span>
<span style="color:#696969;">// no first letter</span>
<span style="font-weight:bold;color:#800000;">string</span><span style="color:#808030;">.</span>IsNullOrEmpty<span style="color:#808030;">(</span>firstlettervalue<span style="color:#808030;">)</span>
<span style="color:#696969;">// first letter does not match and is not 0</span>
|| <span style="color:#808030;">(</span>firstlettervalue <span style="color:#808030;">!</span><span style="color:#808030;">=</span> <span style="color:#800000;">"</span><span style="color:#0000e6;">0</span><span style="color:#800000;">"</span> && <span style="color:#808030;">!</span>linktitle<span style="color:#808030;">.</span>StartsWith<span style="color:#808030;">(</span>firstlettervalue<span style="color:#808030;">,</span> StringComparison<span style="color:#808030;">.</span>InvariantCultureIgnoreCase<span style="color:#808030;">)</span><span style="color:#808030;">)</span>
<span style="color:#696969;">// firstlettervalue == 0 iff title starts with number and first letter is not 0</span>
|| <span style="color:#808030;">(</span><span style="color:#808030;">(</span>firstlettervalue <span style="color:#808030;">=</span><span style="color:#808030;">=</span> <span style="color:#800000;">"</span><span style="color:#0000e6;">0</span><span style="color:#800000;">"</span><span style="color:#808030;">)</span> <span style="color:#808030;">=</span><span style="color:#808030;">=</span> <span style="color:#808030;">(</span>Regex<span style="color:#808030;">.</span>IsMatch<span style="color:#808030;">(</span>linktitle<span style="color:#808030;">,</span> <span style="color:#800000;">"</span><span style="color:#0000e6;">^([a-z]|[A-Z]).*</span><span style="color:#800000;">"</span><span style="color:#808030;">)</span><span style="color:#808030;">)</span><span style="color:#808030;">)</span><span style="color:#808030;">)</span>
<span style="color:#808030;">)</span>
<span style="color:#800080;">{</span>
<span style="color:#696969;">// to simplify the filter web part, the value set will always be </span>
<span style="color:#696969;">// an uppercase letter or 0 for other characters.</span>
<span style="font-weight:bold;color:#800000;">if</span> <span style="color:#808030;">(</span>Regex<span style="color:#808030;">.</span>IsMatch<span style="color:#808030;">(</span>linktitle<span style="color:#808030;">,</span> <span style="color:#800000;">"</span><span style="color:#0000e6;">^([a-z]|[A-Z]).*</span><span style="color:#800000;">"</span><span style="color:#808030;">)</span><span style="color:#808030;">)</span>
<span style="color:#800080;">{</span>
item<span style="color:#808030;">[</span><span style="color:#800000;">"</span><span style="color:#0000e6;">FirstLetter</span><span style="color:#800000;">"</span><span style="color:#808030;">]</span> <span style="color:#808030;">=</span> <span style="color:#808030;">(</span><span style="color:#800000;">"</span><span style="color:#800000;">"</span> <span style="color:#808030;">+</span> linktitle<span style="color:#808030;">[</span><span style="color:#008c00;">0</span><span style="color:#808030;">]</span><span style="color:#808030;">)</span><span style="color:#808030;">.</span>ToUpper<span style="color:#808030;">(</span><span style="color:#808030;">)</span><span style="color:#800080;">;</span>
<span style="color:#800080;">}</span>
<span style="font-weight:bold;color:#800000;">else</span>
<span style="color:#800080;">{</span>
item<span style="color:#808030;">[</span><span style="color:#800000;">"</span><span style="color:#0000e6;">FirstLetter</span><span style="color:#800000;">"</span><span style="color:#808030;">]</span> <span style="color:#808030;">=</span> <span style="color:#800000;">"</span><span style="color:#0000e6;">0</span><span style="color:#800000;">"</span><span style="color:#800080;">;</span>
<span style="color:#800080;">}</span>
item<span style="color:#808030;">.</span>Update<span style="color:#808030;">(</span><span style="color:#808030;">)</span><span style="color:#800080;">;</span>
<span style="color:#800080;">}</span>
<span style="color:#800080;">}</span>
<span style="font-weight:bold;color:#800000;">public</span> <span style="font-weight:bold;color:#800000;">override</span> <span style="font-weight:bold;color:#800000;">void</span> ItemAdded<span style="color:#808030;">(</span>SPItemEventProperties properties<span style="color:#808030;">)</span>
<span style="color:#800080;">{</span>
<span style="font-weight:bold;color:#800000;">base</span><span style="color:#808030;">.</span>ItemAdded<span style="color:#808030;">(</span>properties<span style="color:#808030;">)</span><span style="color:#800080;">;</span>
SetFirstLetter<span style="color:#808030;">(</span>properties<span style="color:#808030;">.</span>ListItem<span style="color:#808030;">)</span><span style="color:#800080;">;</span>
<span style="color:#800080;">}</span>
<span style="font-weight:bold;color:#800000;">public</span> <span style="font-weight:bold;color:#800000;">override</span> <span style="font-weight:bold;color:#800000;">void</span> ItemUpdated<span style="color:#808030;">(</span>SPItemEventProperties properties<span style="color:#808030;">)</span>
<span style="color:#800080;">{</span>
<span style="font-weight:bold;color:#800000;">base</span><span style="color:#808030;">.</span>ItemUpdated<span style="color:#808030;">(</span>properties<span style="color:#808030;">)</span><span style="color:#800080;">;</span>
SetFirstLetter<span style="color:#808030;">(</span>properties<span style="color:#808030;">.</span>ListItem<span style="color:#808030;">)</span><span style="color:#800080;">;</span>
<span style="color:#800080;">}</span>
<span style="color:#800080;">}</span>
<span style="color:#800080;">}</span>
Once this is working, you will want to remove the FirstLetter field from the form. To do this:
1. Enable content type management on the links list
2. Edit the link content type
3. Edit FirstLetter and set it to hidden so it will not appear on forms.
Another option I looked at was using Ishai’s Advanced Content Query web part, as that is already in use on the site and does handle filters properly. However, that would mean replacing the good parts of the out of box list view as well as the bad parts, making it less desirable than the query string filter solution.
If you need to add the paging functionality to another sort of list, filtering on the title column, create FirstLetter as a calculated column with the formula
=IF(ISNUMBER(VALUE(LEFT(Title,1))), "0", LEFT(Title,1))