Crumb Trail Data


In the Navigation Data section it was shown how NavigationData could be passed during a forward Navigation and then accessed via the StateContext Data property. Without being able to pass data during a backward Navigation the Crumb Trail would be of little use. As an example, consider the State Information configured in State Information, and the route:

D1-S1 -> D1-S2 -> D1-S3
If State S2 is expecting to receive an item of data then S1 would pass this in NavigationData when Navigating along Transition T1. If the backward Navigation from S3 to S2, via the Crumb Trail, does not pass this data then an error will occur. The image below shows the NavigationData passing that is required, indicating that S2 need not be aware of the previous State as long as the item of data is passed (forward) from S1 and (backward) from S3. Making S3 aware of this item of data introduces a coupling, between S3 and S2, which should not be there as there is no Transition from S3 to S2, so a better solution is required.

Backward NavigationData passing

The Framework, as Navigations occur, keeps track of the StateContext Data associated with each State passed through and hence builds a context-sensitive Crumb Trail. During any backward Navigation this StateContext Data is retrieved and then passed as NavigationData. Regardless of whether a forward or backward Navigation occurs the data will always be accessible via the StateContext Data property. So, to return to the example above, when Transitioning along T2 a Crumb for D1-S2 will be created containing D1-S2’s StateContext Data, and this will be passed as NavigationData when Navigating backward from S3 to S2 via this Crumb.

StateContext Data is not read only so, although it is initialised with the data passed in via Navigation, the State Navigated to can modify it. And it is this modified Data that will be passed back in when a backward Navigation occurs. The image below demonstrates this idea: State S2 is first reached via a Navigation along Transition T1 passing some data; S2 adds an item to StateContext Data and then Navigates along T2 to S3; a backward Navigation from S3 to S2 occurs and the NavigationData passed in contains all the data passed in the original Transition as well as this new item.

Backward StateContext Data passing

StateContext Data is preserved across PostBacks. It is stored in the ControlState of the StateAdapter configured in the Getting Started section.

Sample Web Site

To demonstrate the usefulness of Crumb Trail Data, there needs to be some StateContext Data added to the Listing State so that it can be restored when navigating back from the Details State.

So, to Listing.aspx add search criteria that will allow the Person list to be filtered based on a partial match on Name and a minimum Date of Birth. This consists of two Text Boxes, a Search Button and a CompareValidator (to ensure a valid date is entered) as shown below.

<asp:Label ID="Label1" runat="server" Text="Name" AssociatedControlID="TextBox1" />
<asp:TextBox ID="TextBox1" runat="server" />
<asp:Label ID="Label2" runat="server" Text="Min Date of Birth" AssociatedControlID="TextBox2" />
<asp:TextBox ID="TextBox2" runat="server" />
<asp:Button ID="Button1" runat="server" Text="Search" />
<asp:CompareValidator ID="CompareValidator1" runat="server" ControlToValidate="TextBox2" EnableClientScript="false" ErrorMessage="date error" Operator="DataTypeCheck" Type="Date" />
To Listing.aspx add a Click handler to the Search Button and set the values from the Text Boxes into the StateContext Data (making sure to add a using to the Navigation namespace) and refresh the GridView, remembering to check for Page validity as shown below. Setting the Search Criteria in StateContext Data ensures it will be retained when using back navigation.

protected void Button1_Click(object sender, EventArgs e)
{
	if (Page.IsValid)
	{
		StateContext.Data["name"] = TextBox1.Text.Length != 0 ? TextBox1.Text : null;
		StateContext.Data["minDateOfBirth"] = TextBox2.Text.Length != 0 ? TextBox2.Text : null;
		GridView1.DataBind();
	}
}
To PersonSearch amend the Linq in the Search method to add a where clause that uses the StateContext Data search criteria to filter the results as shown below.

public IEnumerable Search()
{
	return from p in _People
			where (StateContext.Data["name"] == null || p.Name.ToUpperInvariant().Contains(((string)StateContext.Data["name"]).ToUpperInvariant()))
			&& (StateContext.Data["minDateOfBirth"] == null || p.DateOfBirth >= DateTime.Parse((string)StateContext.Data["minDateOfBirth"]))
			select new
			{
				p.Name,
				p.DateOfBirth,
				Link = StateController.GetNavigationLink("Select", new NavigationData() { { "id", p.Id } })
			};
}
Press F5 and filter the Person List, for example enter ‘bob’ into the Name Text Box and press Search. This should return just one Person. Selecting this Person and then the Hyperlink on the Details.aspx will return to the Listing.aspx Page with the List filtered to show just one row, i.e., the Crumb Trail has remembered the search criteria.

However, the Text Boxes have not retained their values. To remedy this add the Page_Load code shown below to Listing.aspx. Repeating the same test as above now restores the Text Box values.

protected void Page_Load(object sender, EventArgs e)
{
	if (!Page.IsPostBack)
	{
		TextBox1.Text = (string)StateContext.Data["name"];
		TextBox2.Text = (string)StateContext.Data["minDateOfBirth"];
	}
}
To demonstrate StateContext Data is preserved across PostBacks:
  1. Turn off ViewState on the GridView
  2. Press F5 and filter the Person List to show just one row
  3. Enter an invalid date string into the Min Date of Birth Text Box and press Search
Notice a PostBack occurs (as EnableClientScript is set to false on the CompareValidator), the Page is not valid, but the GridView is still filtered. ViewState can even be turned off at the Page directive level (see below) and the test still works.

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Listing.aspx.cs" Inherits="NavigationSample.Listing" EnableViewState="false" %>
To illustrate that Pages need not be Navigation-aware, change the Page_Load method of Details.aspx so it no longer uses Crumb Trail Navigation. Instead use forward Navigation and pass some search criteria in NavigationData as shown below. Pressing F5, selecting a row and selecting the Hyperlink to the Listing.aspx will show a Person List containing only one row and the Text Boxes containing the search criteria.

protected void Page_Load(object sender, EventArgs e)
{
	HyperLink1.NavigateUrl = StateController.GetNavigationLink("Person", new NavigationData()
	{
		{"name", "a"},
		{"minDateOfBirth", "1/1/1970"}
	});
}

Last edited Aug 4, 2013 at 7:20 PM by GrahamMendick, version 5