如果使用AJAX,浏览器的历史按钮是不起作用的,事实上前进和后退只作用于完整的页面而不能用于AJAX的每一个步骤。对于惯用这些功能的用户来说简直是噩梦……
值得庆幸的是AJAX具有识别浏览器浏览历史点的相关能力,它能让浏览器追踪AJAX的步骤并且将这些步骤运用到前进和后退功能里。
先通过一个例子来了解浏览器的历史点是如何工作的。页面中有一个显示公司部门的下拉列表框,选择一个部门,就会显示隶属该部门的雇员姓名的列表框,选择其中一个雇员就会列出该雇员的
资料,如姓名,email,薪水等等。雇员信息保存在Employee类中。
public class Employee
{
public string Name { get; set; }
public string Dept { get; set;}
public string Email { get; set; }
public int Salary { get; set; }
} aspX页面有三个UpdatePanel,如下:
<asp :ScriptManager ID="SManager" runat="server"></asp>
<asp :UpdatePanel ID="DeptUpdPanel" runat="server">
<contenttemplate>
<asp :Panel ID="DeptPanel" runat="server" Visible="True">
</asp><asp :DropDownList ID="DrpDept" runat="server"
AutoPostBack="True"
OnSelectedIndexChanged="DeptDrp_SelectedIndexChanged">
<asp :ListItem Text="Select Department" Selected="true" />
<asp :ListItem Text="Admin" />
<asp :ListItem Text="HR" />
<asp :ListItem Text="Manufacturing" />
</asp>
</contenttemplate></asp>
<asp :UpdatePanel ID="EmpUpdPanel" runat="server">
<contenttemplate>
<asp :Panel ID="EmpPanel" runat="server" Visible="False">
</asp><asp :DropDownList ID="DrpEmployee" runat="server"
AutoPostBack="True"
OnSelectedIndexChanged="EmpDrp_SelectedIndexChanged">
</asp>
</contenttemplate></asp>
<asp :UpdatePanel ID="DetailUpdPanel" runat="server">
<contenttemplate>
<asp :Panel ID="DetailPanel" runat="server" Visible="False">
Department: <asp :Label runat="server" ID="LblDept"/></asp><br />
Name: <asp :Label runat="server" ID="LblName"/></contenttemplate></asp><br />
Email: <asp :Label runat="server" ID="LblEmail"/><br />
Salary: <asp :Label runat="server" ID="LblSalary"/<br>
</asp>
代码构造很简单,含有下拉框改变SelectedIndex处理事件调用。DeptDrp_SelectedIndexChanged事件函数生成部门雇员数据,并将数据绑定到名为Employees的list类型的列表中,处理数据
程序使用基于LINQ
查询来检索被选部门的雇员数据。
protected void DeptDrp_SelectedIndexChanged(object sender, EventArgs e)
{
if (Session["Employees"] == null)
{
Employees = new List<employee>(new Employee{
new Employee(){Name = "David",
Dept="HR", Email = "David@org.com",
Salary = 2000},
new Employee(){Name = "George",
Dept="Admin", Email = "George@org.com",
Salary = 2500},
new Employee(){Name = "Bill",
Dept="Admin", Email = "Bill@org.com",
Salary = 6000},
new Employee(){Name = "Henry",
Dept="Manufacturing", Email = "Henry@org.com",
Salary = 4750},
new Employee(){Name = "Michael",
Dept="Manufacturing", Email = "Michael@org.com",
Salary = 8000},
});
Session["Employees"] = Employees;
}
else
{
Employees = (List</employee><employee>)Session["Employees"];
}
DeptPanel.Visible = false;
EmpPanel.Visible = true;
var Emp = from E in Employees
where E.Dept == DrpDept.SelectedValue
select new { E.Name };
DrpEmployee.DataSource = Emp;
DrpEmployee.DataValueField = "Name";
DrpEmployee.DataBind();
DrpEmployee.Items.Insert(0, "Select Employee");
} EmpDrp_SelectedIndexChanged事件函数用于显示被选雇员的
资料。
protected void EmpDrp_SelectedIndexChanged(object sender, EventArgs e)
{
Employees = (List</employee><employee>)Session["Employees"];
EmpPanel.Visible = false;
DetailPanel.Visible = true;
var Emp = from E in Employees
where E.Name == DrpEmployee.SelectedValue
select new { E.Name, E.Email, E.Dept, E.Salary };
foreach (var EDetail in Emp)
{
LblDept.Text = EDetail.Dept;
LblEmail.Text = EDetail.Email;
LblName.Text = EDetail.Name;
LblSalary.Text = EDetail.Salary.ToString("C");
}
} 当生成并运行项目后,你会看到浏览器的前进后退按钮是失效的,因为浏览器目前无法追踪AJAX的所有步骤。
要解决这个问题,不得不在项目中引入浏览器的历史点。要保持历史,首先必须启用ScriptManager控件的相关设置,还需要具有响应浏览器前进后退按钮按下时的事件处理程序。ScriptManager控件定义如下:
<asp :ScriptManager ID="SManager" runat="server" EnableHistory=true OnNavigate="History_Navigate" />
此外还需要为两次AJAX异步postback之后创建历史点,一次是选定了部门,另一次是选定了雇员。为此需要创建一个Add_History方法:
private void Add_History(string Title)
{
NameValueCollection State = new NameValueCollection();
State.Add("DeptIndex", DrpDept.SelectedIndex.ToString());
State.Add("EmployeeIndex", DrpEmployee.SelectedIndex.ToString());
SManager.AddHistoryPoint(State, Title);
} [/sharp]
从代码可见ScriptManager的AddHistoryPoint的调用,该方法将当前被选中的部门值和当前被选中的雇员值连同页面标题一起保存在NameValueCollection类型的State中。用于提供浏览器前进后退时的呈现。
接下来就是调用这个Add_History方法了。在DeptDrp_SelectedIndexChanged(选取了部门)事件处理函数体中添加如下代码:
[csharp]
if (SManager.IsInAsyncPostBack && !SManager.IsNavigating)
Add_History("Employee List: " + DrpDept.SelectedValue);
!SManager.isNavigating用于强调当前的postback,并不会因用户点击了浏览器的前进后退的行为造成的多重调用。同样的方法用于EmpDrp_SelectedIndexChanged(选取了雇员)事件处理函数里:
if (SManager.IsInAsyncPostBack && !SManager.IsNavigating)
Add_History("Employee Detail:" + DrpEmployee.SelectedItem.Text);
注意,当DrpEmployee.SelectedIndex也就是被选中的部门的值是0时,应该将EmployeeIndex也设为0。然而目前依照Add_History方法来看,Deptndex和EmployeeIndex都是需要有值的。而且页面在首次加载时并没有任何状态,因此Deptndex和EmployeeIndex都是null。于是当History_Navigate事件被浏览器动作调用时,需要进行一些处理:
protected void History_Navigate(object sender, HistoryEventArgs e)
{
NameValueCollection State = e.State;
string DeptIndex = State["DeptIndex"];
string EmployeeIndex = State["EmployeeIndex"];
//Set DeptIndex = "0" when its null
DeptIndex = string.IsNullOrEmpty(DeptIndex) ? "0" : DeptIndex;
EmployeeIndex = string.IsNullOrEmpty(EmployeeIndex) ? "0" : EmployeeIndex;
//Dept List View
if (DeptIndex == "0")
{
DeptPanel.Visible = true;
EmpPanel.Visible = false;
DetailPanel.Visible = false;
}
//Employee List View
if (DeptIndex != "0" && EmployeeIndex == "0")
{
DeptPanel.Visible = false;
EmpPanel.Visible = true;
DetailPanel.Visible = false;
}
//Employee Detail View
if (EmployeeIndex != "0")
{
DeptPanel.Visible = false;
EmpPanel.Visible = false;
DetailPanel.Visible = true;
}
} 该事件通过HistoryEventArgs获得当前页面状态,并而根据状态中DeptIndex和EmployeeIndex的值来隐藏或显示对应状态的panel。
目前已经成功地将AJAX调用引入浏览器的历史,并能在前进和后退中控制这些调用。很重要的一点,这里所说的历史点如同浏览的URL和历史状态的保存方式一样,最大字节为1024。所以要谨慎使用,建议保存最低限度的信息。