Progressive Enhancement

Progressive enhancement is a web design strategy that allows everyone to access the basic content whilst providing an enhanced experience for supporting clients.

ASP.NET Ajax is a form of progressive enhancement since, if the browser supports Ajax then partial page rendering is used, otherwise it defaults to the full page post back model. However, ASP.NET Ajax only operates on posts and so only Buttons and ImageButtons can be used (LinkButtons are not suitable for progressive enhancement as they require javascript).

The Navigation Framework does not care whether a PostBack or refresh Navigation occurred as long as the StateContext Data is the same, illustrated in the following image.
PostBack or Refresh Navigation
PostBack or Refresh Navigation

So the Pager, Sorter, NavigationHyperLink and HyperLink controls can be configured so that they render both as Hyperlinks (javascript disabled) and as javascript PostBacks, as detailed in the table below. They use a technique know as Hijax whereby they render as normal Hyperlinks but, with javascript enabled, the default behavior is hijacked and turned into a PostBack:

<a href=”URL” onclick=”doPostBack();return false”/>
Configuration for dual Hyperlink and PostBack behavior
Control Configuration
Pager QueryStringField must be populated and PostBackHyperLink set to true
Sorter Navigate and PostBackHyperLink must both be true
NavigationHyperLink Direction must be set to Refresh and PostBack to true
HyperLink NavigateUrl must be set using {RefreshPostBack} markup syntax and the RefreshHyperLinkAdapter registered as a HyperLink's ControlAdapter

With javascript disabled these controls behave as Hyperlinks and so bookmarking and browser history work as usual. Also, it works seamlessly with SEO (Search Engine Optimization) as Web Crawlers can follow the Hyperlink.

With javascript enabled, these controls behave as PostBack links and so, by wrapping them inside an UpdatePanel, ASP.NET Ajax can be used to provide partial page rendering. In the ASP.NET Ajax History section, bookmarking and browser history support will be added for true progressive enhancement.

Similarly to the Navigation framework's integration with DataBinding, where data bound controls automatically refresh when there is a change to their StateContext Data, there is a NavigationDataTrigger that allows UpdatePanels to do the same.

Sample Web Site

Change the Pager by setting PostBackHyperLink to true as shown below.

<cc1:Pager ID="Pager1" runat="server" QueryStringField="q" PostBackHyperLink="true">
	<Fields>
		<asp:NextPreviousPagerField ShowFirstPageButton="true" ShowLastPageButton="true" />
	</Fields>
</cc1:Pager>
Pressing F5 and clicking the paging links will cause POST requests. Turning off javascript and clicking the paging links will cause GET requests. Both provide a fully functioning back button, but only the javascript disabled scenario provides the ability to bookmark, because with javascript enabled the URL never changes when the links are clicked (this will be rectified in the ASP.NET Ajax History section).

The same can be done for the Sorter, by setting its Navigate and PostBackHyperLink properties (see below)

<cc1:Sorter ID="Sorter1" runat="server" Text="Name" SortBy="Name" Navigate="true" PostBackHyperLink="true" />
To achieve the same for the change page size HyperLinks a ControlAdapter must be registered for the HyperLink by adding a line to the Navigation.browser file (see below).

<controlAdapters>
	<adapter controlType="System.Web.UI.Page" adapterType="Navigation.StateAdapter, Navigation" />
	<adapter controlType="System.Web.UI.WebControls.HyperLink" adapterType="Navigation.RefreshHyperLinkAdapter, Navigation" />
</controlAdapters>
Then by assigning their NavigateUrl properties using RefreshPostBack markup instead of RefreshLink markup, below, they will issue post backs if javascript is enabled.

<asp:HyperLink ID="HyperLink1" runat="server" NavigateUrl="{RefreshPostBack &startRowIndex,maximumRows=5}" Text="5" />
<asp:HyperLink ID="HyperLink2" runat="server" NavigateUrl="{RefreshPostBack &startRowIndex,maximumRows}" Text="10" />
The self-same effect could be produced using NavigationHyperLinks by setting its PostBack property and is shown below.

<cc1:NavigationHyperLink ID="HyperLink1" runat="server" ToData="{NavigationData &startRowIndex,maximumRows=5}" Direction="Refresh" Text="5" PostBack="true" />
<cc1:NavigationHyperLink ID="HyperLink2" runat="server" ToData="{NavigationData &startRowIndex,maximumRows}" Direction="Refresh" Text="10" PostBack="true" />
To the Listing.aspx add ASP.NET Ajax functionality by including a ScriptManager and wrap an UpdatePanel around all the controls as shown in below.

<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
	<ContentTemplate>
		<asp:FormView ID="FormView1" runat="server" DataSourceID="NavigationDataSource1" DefaultMode="Edit">
		<%--other controls elided--%>
		<asp:Literal ID="Literal1" runat="server" Text="{NavigationData totalRowCount,Total Count {0}}" />
	</ContentTemplate>
</asp:UpdatePanel>
Pressing F5 and filtering, sorting and paging the Person List illustrates the Ajax PartialPageRequests in action. However, the browser back button remains disabled (this will be rectified in the ASP.NET Ajax History section).

Currently the UpdatePanel is set to always Update, as would be expected with this being the only UpdatePanel on the Page. However, to show how the NavigationDataTrigger works, change the UpdateMode to Conditional and set the ChildrenAsTriggers to false as shown below.

<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="false">
Pressing F5 and sorting, for example, will not update the Person List. To fix this, add NavigationDataTriggers, one for each piece of StateContext Data that requires the UpdatePanel to refresh, as shown below.

<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional" ChildrenAsTriggers="false">
	<ContentTemplate>
		<%--controls elided--%>
	</ContentTemplate>
	<Triggers>
		<cc1:NavigationDataTrigger Key="name" />
		<cc1:NavigationDataTrigger Key="minDateOfBirth" />
		<cc1:NavigationDataTrigger Key="sortExpression" />
		<cc1:NavigationDataTrigger Key="startRowIndex" />
		<cc1:NavigationDataTrigger Key="maximumRows" />
	</Triggers>
</asp:UpdatePanel>
Pressing F5 and filtering, sorting and paging the Person List all result in a refresh of the UpdatePanel.

Last edited Aug 4, 2013 at 7:30 PM by GrahamMendick, version 6