65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET 中 AJAX 技术与框架简介

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.90/5 (135投票s)

2006年8月2日

CPOL

21分钟阅读

viewsIcon

556865

downloadIcon

2542

本文向 ASP.NET 开发人员介绍了 AJAX,使用 ASP.NET Atlas、ASP.NET 回调、Ajax.Net、Anthem.Net 和 MagicAjax.Net 以不同方式实现了一个示例网页。

目录

引言

现在,您很可能听说过在 Web 开发中使用 AJAX。这个缩写词是由 Jesse James Garret 在他的文章《AJAX:Web 应用程序的新方法》中创造的。此后,出现了几个框架来支持 AJAX Web 开发。在本文中,我们将研究一些可供 ASP.NET 程序员使用的框架。本文的重点将放在 Microsoft 的 ASP.NET Atlas 上,它正在成为 ASP.NET 中 AJAX 开发最重要的框架。我们将通过一个电子商务网站中的网页示例来研究不同的框架和 AJAX 的优点。让我们从查看示例的详细信息开始。

示例网页

我们将开发一个简单的网页,向用户展示一个列表框中显示的电子商务网站销售的商品列表。当用户从列表框中选择一个商品时,列表框下面的文本会显示仓库中该商品的库存数量。为了正确说明 AJAX 的优点,列表框将出现在几段“Lorem Ipsum”文本下方。以下是示例网页的屏幕截图。

Screenshot

该示例使用 SQL Server Express 数据库。您可以从 SQL Server 网站免费下载 SQL Server Express。商品列表维护在一个名为 Items 的表中。该表有三列:ItemIDItemNameItemQuantity。列表框通过绑定到配置了 Items 表的 SqlDataSource 来填充。表的 ItemID 字段绑定到列表项值,ItemName 字段绑定到显示文本。页面标记如下所示。

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>The Classical ASP.NET Way</title>
</head>
<body>
   <div class="testContent">
       .
       Paragraphs of Lorem Ipsum     .
    </div>
    <form id="form1" runat="server">
        <div>
            <label for="ItemList" accesskey="I">
                Items:</label>
            <asp:ListBox runat="server" ID="ItemList"                
            DataSourceID="ItemsSource" DataTextField="ItemName"
                DataValueField="ItemID" EnableViewState="False">         
            </asp:ListBox>
            <div id="ItemQuantityDisplay" runat="server">
            The item quantity will be displayed here   </div>
            <asp:SqlDataSource ID="ItemsSource" runat="server"
                ConnectionString="<%$ ConnectionStrings:Items %>"
                SelectCommand="SELECT [ItemName], [ItemID] FROM [Items]">
            </asp:SqlDataSource>
        </div>
    </form>
 </body>
</html>

让我们首先看看如何在经典的 ASP.NET 中编写网页代码。

经典的 ASP.NET 实现

我们的目标是在用户在列表框中选择一个商品时显示该商品的库存数量。我们可以通过更改 ItemQuantityDisplay div 元素的文本来实现这一点。ItemQuantityDisplay 具有属性 runat="server",它允许服务器代码修改 div 元素的文本和其他属性。我们将列表框的 AutoPostback 属性设置为 true,以便在列表框选择更改时,表单自动回发到服务器。

protected void Page_Load(object sender, EventArgs e) {
   this.MaintainScrollPositionOnPostBack = true;
   ItemQuantityDisplay.Visible = false;
}

protected void ItemList_SelectedIndexChanged(object sender, EventArgs e) 
{
   ItemQuantityDisplay.Visible = true;
   try {
      ItemQuantityDisplay.InnerText =
      String.Format(" {0} in stock",
             Warehouse.GetItemQuantity(ItemList.SelectedValue));
   } catch (Exception ex) {
      Trace.Write("GetItemQuantity Error: " + ex.Message);
      ItemQuantityDisplay.InnerText = "Error retrieving quantity";
      ItemQuantityDisplay.Attributes["class"] = "Error";
   }
}

列表框上方有几段文本,因此用户可能需要滚动才能到达列表框并更改其选择。我们将 MaintainScrollPositionOnPostBack 设置为 true,以便在回发完成后,页面的滚动位置与用户离开时的位置相同;否则,用户将不得不滚动才能看到显示商品数量的文本。

ItemList_SelectedIndexChanged 中,我们通过调用 Warehouse.GetItemQuantity 方法获取仓库中的商品数量,该方法运行 SQL 查询以获取商品数量。我们通过修改 InnerText 属性来更改 ItemQuantityDisplay 的文本,该属性在没有错误发生时设置为显示库存商品数量。如果发生错误,我们将 ItemQuantityDisplay 的 CSS 类修改为“Error”(在单独的样式表文件中定义),并将文本设置为向用户显示发生错误。

现在,让我们看看用户浏览网页时的体验。根据浏览器窗口的高度,用户可能需要或不需要滚动才能到达列表框。一旦他到达列表框并选择一个商品,他会看到网页短暂闪烁,并在列表框下面的文本中看到商品的库存数量。他会看到在他选择商品后重新加载页面时,网页会向下滚动。这种交互绝不能称为连续的,尽管 MaintainScrollPositionOnPostback 试图使回发的非连续动作看起来连续。现在,让我们看看相同应用程序的 AJAX 版本。

使用 AJAX 进行连续交互

AJAX 的主要承诺是为用户在使用网站时提供连续的交互。让我们看看如何实现这一点。在我们的应用程序的 AJAX 版本中,我们将使用 XMLHttpRequest 对象获取所选商品的库存数量,并使用客户端脚本更改 ItemQuantityDisplay 的文本。由于页面不像传统 Web 应用程序那样完全重新加载,我们可以期待更流畅的交互。

为了让 JavaScript 获取商品的库存数量,我们需要在我们的网站中提供一个端点,可以在此获取此信息。我们需要一个可以获取商品数量的 URL。最直接的方法是通过 HTTP GET 协议访问 ASP.NET Web 服务方法。URL 形式如下:

http://hostname/path/<webservice>/<methodname>?<name1>=<value1>&<name2>=<value2>

您可能需要在 *web.config* 文件中启用对 ASP.NET Web 服务的 HTTP GET 访问。这可以通过向 *web.config* 添加配置条目来完成,如下面的清单所示:

<configuration>
  <system.web>
    <webServices>
      <protocols>
        <add name="HttpGet"/>

接下来,我们创建一个 Web 服务,该服务提供一个方法来获取仓库中商品的库存数量。代码如下所示:

public class WarehouseService : System.Web.Services.WebService {
    
    [WebMethod]
    public int GetItemQuantity(string itemID) 
    {
        return Warehouse.GetItemQuantity(itemID);
    }
}

假设 Web 服务位于您的本地计算机上,并且您要访问其数量的商品 ID 是 *79ec4891-a73d-4fcc-ade9-2c2a47f7b2df*,您可以使用以下 URL:

http::///WareHouseService.asmx/GetItemQuantity?
      itemID=79ec4891-a73d-4fcc-ade9-2c2a47f7b2df

这将返回以下形式的响应:

<int xmlns=”…”>85</int>

现在我们准备编写应用程序的其余部分。由于启用了 AJAX 的网页使用 JavaScript 异步与服务器通信,因此为用户提供一些反馈至关重要。许多 AJAX 网站都显示带有文本消息的动画 GIF。一些公共领域的图像可以从此处下载。我们选择一个合适的图像,并修改 ItemQuantityDisplay,如清单所示:

<div>                                                
  <span id="ItemQuantityDisplay"></span>                     
  <span id="ProgressDisplay" style="display: none">
      <img src="images/loading.gif" alt="Loading" />
       Checking Stock...
  </span>
</div>

我们现在有一个 div,它包含两个 span,ID 分别为 ItemQuantityDisplayProgressDisplayProgressDisplay 显示 span 包含一个动画 GIF 图像,其右侧有文本“正在检查库存”。ProgressDisplay 最初是隐藏的。在异步操作进行时显示反馈,仅仅是隐藏 ItemQuantityDisplay span 并显示 ProgressDisplay span 的问题;当异步操作完成时,ProgressDisplay span 被隐藏,ItemQuantityDisplay span 被显示。

接下来,我们需要添加 JavaScript 代码来处理 ItemList 的选择更改事件并异步调用服务器。这可以嵌入在页面中,也可以放在单独的文件中。在 AJAX 应用程序中,单独的文件方法是首选方式,因为脚本代码库往往很大。单独文件中的脚本代码意味着浏览器可以独立于页面缓存代码,从而节省一些带宽。在我们的示例中,我们将代码放在一个单独的文件中。

XMLHttpRequest 对象在 Internet Explorer 7.0、Mozilla Firefox、Opera 和 Safari 中作为内置对象提供。在 Internet Explorer 6.0 中,它作为 ActiveX 对象提供。下面的清单中显示的 JavaScript 代码考虑了这种差异。

if (!window.XMLHttpRequest){
   window.XMLHttpRequest = function(){
     return new ActiveXObject("Microsoft.XMLHTTP");
   }
}

此代码首先检查 XMLHttpRequest 对象的可用性。如果不可用,则向 window 对象添加一个类型为 function 的新属性。该函数实例化一个 prog ID 为 Microsoft.XMLHTTP 的新 ActiveX 对象并返回该对象。为简单起见,我们忽略了那些可能没有内置 XMLHttpRequest 对象且不支持 ActiveX 对象的浏览器。此代码允许我们在所有浏览器上实例化 XMLHttpRequest 对象。

var xr = new XMLHttpRequest();

这是因为 window 对象的属性和方法被认为是全局的。

现在,我们需要处理 ItemList 的更改事件。Mozilla Firefox 和其他兼容 W3C DOM Level 2 的浏览器提供了一个名为 addEventListener 的方法,而 Internet Explorer 提供了一个名为 attachEvent 的方法。因此,我们为更改事件添加事件处理程序的代码将如下所示:

if (itemList.attachEvent) {
  itemList.attachEvent("onchange", itemList_OnChange);
}
else {
  itemList.addEventListener("change", itemList_OnChange, false);
}

代码检查 attachEvent 方法是否存在,如果存在,则调用 attachEvent 方法,否则调用 addEventListener。还要注意事件名称的不同;在 Internet Explorer 中是 onchange,在 Mozilla FireFox 中是 change。欢迎来到浏览器不兼容的世界!

我们应用程序的主要工作将在 itemList_OnChange 函数中完成,如下所示:

function itemList_OnChange(){
    var xr = new XMLHttpRequest();
    xr.open("GET", 
"WarehouseService.asmx/GetItemQuantity?itemID=" + itemList.value, true);
    xr.onreadystatechange = function() {
        if (xr.readyState == 4) {                
            if (xr.status == 200) { //Successful Request

                var doc = xr.responseXML;
                var qty;
                
                if (doc.evaluate) {           
                    qty = doc.evaluate("//text()", doc, 
                    //XML Parsing in Mozilla

                                null,
                                XPathResult.STRING_TYPE, null).stringValue;
                }
                else {
                   //XML Parsing in IE

                   qty = doc.selectSingleNode("//text()").data;    
                }
                
                itemQuantityDisplay.innerHTML = qty + " in stock";
                itemQuantityDisplay.className = "";
            } else {
                itemQuantityDisplay.innerHTML = "Error retrieving quantity";
                itemQuantityDisplay.className = "Error";
            }
        
            //Hide Progress

            itemQuantityDisplay.style.display = "";
            progressDisplay.style.display = "none";
        }
    }
    xr.send(null);
    
    //Display progress

    progressDisplay.style.display = "";
    itemQuantityDisplay.style.display = "none";
}

我们可以看到使用 XMLHttpRequest 对象涉及的四个步骤:

  1. 实例化 XMLHttpRequest 对象。
  2. 调用 XMLHttpRequest 对象的 open 方法。第一个参数是要使用的 HTTP 方法,可以是 GET 或 POST。第二个参数是您希望使用该对象访问的资源的 URL。
  3. onreadystatechange 事件提供一个处理程序。该处理程序将在对象状态更改时由对象调用。大部分工作都在此处理程序中完成。在列表中,我们分配了一个匿名函数来处理此事件。
  4. 调用 send 方法。send 方法可以以字符串形式获取请求内容。这对于使用 HTTP POST 向服务器发布很有用。

当 HTTP 请求结束时,无论成功还是出错,XMLHttpRequest 对象的 readyState 都设置为 4,表示完成。您必须使用返回 HTTP 状态码之一的 status 属性来判断请求是否成功。状态码 200 表示成功。请求完成后,可以通过 responseXML 属性获取 HTTP 响应,该属性将响应作为 XML DOM 文档返回(如果响应内容是 XML),或者通过 responseText 属性获取响应,该属性以纯文本形式返回响应。

在我们的例子中,响应是一个 XML 文档,我们需要从 XML DOM 文档中提取商品数量。由于商品数量作为根文档的文本发送,因此 XPATH //text() 对我们有用。使用 XPath 的确切方法对于 Internet Explorer 和 Mozilla Firefox 是不同的。在 Internet Explorer 中,您可以使用 selectSingleNode 函数,该函数在 Mozilla 中不可用,您必须使用 evaluate 方法。幸运的是,这是我们必须考虑跨浏览器兼容性问题的最后一段代码。

我们在事件处理程序代码中做的另一件有趣的事情是通过操纵元素的 styledisplay 属性来隐藏和显示进度。当发送 HTTP 请求时,我们隐藏 ItemQuantityDisplay 并显示 ProgressDisplay,当请求完成时,我们做相反的操作。

让我们从用户交互的角度检查应用程序,并将其与上一节中的经典 ASP.NET 应用程序进行比较。当用户滚动到商品列表并选择一个商品时,他会看到一个动画,其右侧带有文本“正在检查库存...”。动画在一段时间后消失,用户会看到商品数量或错误消息。没有屏幕闪烁,滚动位置也没有改变。这可以描述为连续交互。

这种连续交互是有代价的。代价是我们必须编写的客户端代码量。幸运的是,客户端代码可以简化。通过将通用代码封装在框架中可以实现简化。幸运的是,ASP.NET 回调提供了一个基本的框架,用于从客户端脚本调用服务器端代码。我们将在下一节中进行探讨。

使用 ASP.NET 回调实现 AJAX

ASP.NET 2.0 具有一个名为回调的功能,它允许控件在不进行完全回发的情况下调用页面上的服务器端代码。回调机制由 TreeView 控件、GridView 控件和 DetailsView 控件使用。TreeView 控件使用回调按需展开节点;GridView 使用回调进行排序和分页;DetailsView 控件使用回调进行分页。

使用回调的步骤如下:

  1. 在控件或 Page 类中实现 ICallbackEventHandler 接口。该接口有两个方法:RaiseCallbackEventGetCallbackResultRaiseCallbackEvent 接受一个字符串参数,该参数由调用回调的客户端脚本提供;GetCallbackResult 返回一个字符串值,该值传递给客户端脚本。
  2. 生成将调用回调的客户端脚本。这可以通过调用 ClientScriptManagerGetCallbackEventReference 方法来生成。ClientScriptManager 的实例可从 Page 类的 ClientScript 属性中获取。
  3. 编写调用在步骤 2 中生成的客户端脚本代码。这可能在事件处理程序中完成。

以下清单显示了 ICallbackEventHandler 的实现:

public string GetCallbackResult()
{
   return callbackResult;
}

public void RaiseCallbackEvent(string eventArgument)
{
  try {
     callbackResult = Warehouse.GetItemQuantity(eventArgument).ToString();
  }
  catch (Exception e)
  {
     Trace.Write(e.Message);
     throw new Exception("Error checking quantity");
  }
}

RaiseCallbackEvent 函数的 eventArgument 参数从客户端脚本传递。在我们的示例中,这将是 ItemList 列表框中选定商品的 ItemIDRaiseCallbackEvent 方法实现只需从 eventArgument 参数获取商品数量并将其放入 Page 的字符串成员变量中。我们需要一个成员变量来存储结果,因为我们需要在 GetCallbackResult 方法中返回它。

ICallbackEventHandler 的实现只是故事的一部分。接下来,我们需要生成实际调用脚本的客户端脚本。这在 PageLoad 事件处理程序中完成,如下所示:

protected void Page_Load(object sender, EventArgs e) {
  if (IsCallback)
      return;

  string callBackFunctionCall = 
              ClientScript.GetCallbackEventReference(
              this, 
              "getSelectedItemID()", 
              "onCallbackComplete",         
              null, 
              "onCallbackError",     
              true
    );
      
  Page.ClientScript.RegisterClientScriptBlock(    
            GetType(), 
            "CallBack",     
            "function DoClientCallBack() { "     
            + callBackFunctionCall + "} ",         
            true        
        );
}

生成客户端代码是一个两步过程。首先,我们调用 GetCallbackEventReference 方法。此方法接受六个参数:

  1. 第一个参数是实现 ICallbackEventHandler 的类的实例。在我们的示例中,这将是 Page 本身。
  2. 第二个参数是需要传递给 RaiseCallbackEvent 方法的值。该参数应该是一个求值为字符串的 JavaScript 表达式。在我们的示例中,我们需要传递列表框中选定商品的值。为此,我们创建一个函数 getSelectedItemID,它返回列表框中选定商品的值。我们在此参数中传递字符串 getSelectedItemID(),它求值为该函数的返回值。
  3. 第三个参数是回调完成时要调用的客户端脚本函数。此函数将传递从 GetCallbackResult 返回的字符串值。
  4. 第四个参数是可与回调实例关联的上下文。如果存在许多回调,则此参数可用于区分它们。此参数必须是 JavaScript 表达式。
  5. 第五个参数是在回调过程中发生错误时调用的 JavaScript 函数的名称。
  6. 最后一个参数是一个布尔值,指示回调应该是同步还是异步。由于我们希望执行异步操作,因此我们将 true 作为此参数的值传递。

GetCallbackEventReference 的返回值是一个 JavaScript 表达式,它向服务器进行异步调用。我们将其放在一个名为 DoClientCallBack 的单独 JavaScript 函数中,该函数将反过来由 ItemList 元素的 change 事件处理程序调用。我们使用 ClientScript.RegisterClientScriptBlock 函数来形成 JavaScript 函数并注册内联脚本块。这完成了我们在服务器上必须做的工作。接下来,我们转到客户端代码。

像往常一样,我们需要为 ItemList 控件的 change 事件附加一个事件处理程序。代码如下所示:

//Callback success

function onCallbackComplete(result, context){
  progressDisplay.style.display = "none";

  itemQuantityDisplay.className = "";
  itemQuantityDisplay.style.display = "";
  itemQuantityDisplay.innerHTML = result + " in stock";
}

//Callback error

function onCallbackError(){
  progressDisplay.style.display = "none";

  itemQuantityDisplay.className = "Error";
  itemQuantityDisplay.style.display = "";
  itemQuantityDisplay.innerHTML = "Error retrieving quantity";
}

function itemList_OnChange()        
{
  document.getElementById("ProgressDisplay").style.display = "";
  document.getElementById("ItemQuantityDisplay").style.display = "none";
  DoClientCallBack();
}

此示例中的用户交互与上一节中介绍的相同,但我们可以看到客户端代码大大简化了,因为我们不再有任何代码来解析 XML 或使用 XMLHttpRequest 对象。ASP.NET 回调机制处理低级调用;但是,我们确实不得不在服务器上添加额外的代码。

AJAX 遇见 Atlas

Atlas 的构建是为了简化 AJAX 应用程序的开发。Atlas 的另一个巨大优势是它使您的代码与浏览器兼容。您不必担心浏览器之间的差异;大多数情况下,Atlas 会处理好这些差异。我们将在下一节中看到这是如何实现的。

从 JavaScript 使用 Web 服务

有多种方法可以在 Atlas 中实现我们的示例。Atlas 的一个核心功能是能够从 JavaScript 代码调用 Web 服务。首先,我们将使用 Atlas 的这一功能。由于我们已经有一个 Web 服务,它是在前几节的示例中开发的,因此我们的任务大大简化了。

使用 Atlas 的第一步是将 Atlas 脚本引用添加到网页中。这是通过向您打算使用 Atlas 的网页添加 ScriptManager 控件来完成的。ScriptManager 控件负责为 Atlas 框架脚本生成适当的脚本引用。它还可以为 Web 服务代理(由 Atlas 自动生成)以及可能依赖于 Atlas 的自定义脚本生成脚本引用。以下清单演示了 Atlas ScriptManager 控件的使用。

<atlas:ScriptManager EnableScriptComponents="false" ID="ScriptManager1" 
    runat="server">
 <Services>
    <atlas:ServiceReference 
       GenerateProxy="true"     
       Path="WarehouseService.asmx" />
 </Services>
 <Scripts>
    <atlas:ScriptReference 
        Path="ScriptLibrary/AtlasExample.js" />
 </Scripts>
</atlas:ScriptManager>

清单中显示的代码是您需要添加到网页中的所有代码。其余代码在一个 JavaScript 文件中,如下所示:

var itemList, itemQuantityDisplay, progressDisplay;

function onCallbackComplete(result){
    progressDisplay.style.display = "none";

    itemQuantityDisplay.className = "";
    itemQuantityDisplay.style.display = "";
    itemQuantityDisplay.innerHTML = result + " in stock";
}

function onCallbackError(){
    progressDisplay.style.display = "none";

    itemQuantityDisplay.className = "Error";
    itemQuantityDisplay.style.display = "";
    itemQuantityDisplay.innerHTML = "Error retrieving quantity";
}

function itemList_OnChange(){
    progressDisplay.style.display = "";
    itemQuantityDisplay.style.display = "none";
    
    //Invoking the web service

    WarehouseService.GetItemQuantity(itemList.value,
       onCallbackComplete,  
       onCallbackError, 
       onCallbackError);
}

Sys.Runtime.load.add(function() {
    itemList = document.getElementById("ItemList");
    itemQuantityDisplay = document.getElementById("ItemQuantityDisplay");
    progressDisplay = document.getElementById("ProgressDisplay");
    
    //Attaching the event handler

    itemList.attachEvent("onchange", itemList_OnChange);
});

这里需要注意的主要一点是调用 Web 服务的简便性。Web 服务方法在 JavaScript 中作为常规函数调用。结果也同样容易获得。Atlas 框架负责 URL 的构建、向 Web 服务发送调用以及解析返回值。XMLHttpRequest 对象的详细信息完全被屏蔽了。

上述清单中另一个值得注意的地方是使用 attachEvent 函数添加事件处理程序。对于不同的浏览器没有特别的考虑。该代码可以在 Mozilla Firefox 和 Internet Explorer 中正常工作。这一切都得益于 Atlas 的跨浏览器兼容层。

从 JavaScript 调用页面方法

Atlas 启用 JavaScript 代码调用服务器代码的另一种方式是通过 Page 方法。您可以在 Page 类中的方法上应用 WebMethod 属性,此方法将自动对 JavaScript 代码可用。让我们将我们的示例转换为使用 Page 方法。

首先,我们需要用 WebMethod 属性标记一个方法:

[System.Web.Services.WebMethod]
public int GetItemQuantity() {
   return Warehouse.GetItemQuantity(ItemList.SelectedValue);
}

请注意 ItemList.SelectedValue 的使用。在 Page 方法中,所有控件值都可以像在回发中一样被访问。因此,当您在方法中需要其他服务器控件的值时,请使用页面方法。JavaScript 代码将与上一节中几乎相同,只是实际的 Web 方法调用不同。这是上一节中进行调用的方式:

//Invoking the web service

WarehouseService.GetItemQuantity(itemList.value,
   onCallbackComplete,  
   onCallbackError, 
   onCallbackError);

这是您将如何调用页面方法:

PageMethods.GetItemQuantity(onCallbackComplete, 
                           onCallbackError, 
                           onCallbackError);

到目前为止我们考虑的所有不同方法,除了回发示例,都要求我们使用 JavaScript。Atlas 提供了一个名为 UpdatePanel 的服务器控件,它允许您使用很少或根本不需要 JavaScript 来开发 AJAX 应用程序。让我们看看如何在我们的示例中使用 UpdatePanel

使用 UpdatePanel 服务器控件

要使用 UpdatePanel,您需要将需要在 AJAX 调用后更新的内容放置在 UpdatePanel 内部。由于我们希望 ItemQuantityDisplay div 被更新,我们将其放置在 UpdatePanel 内部。

<atlas:UpdatePanel ID="ItemQunatityDisplayPanel" Mode="conditional" 
       runat="server">
   <ContentTemplate>
      <div id="ItemQuantityDisplay" runat="server">
      </div>
   </ContentTemplate>
   <Triggers>
      <atlas:ControlEventTrigger 
           ControlID="ItemList" 
           EventName="SelectedIndexChanged" />
   </Triggers>
</atlas:UpdatePanel>

如我们所见,UpdatePanel 声明有两部分:

  1. ContentTemplate 包含服务器控件的 ASP.NET 标记和需要动态更新的 HTML。
  2. Triggers 部分描述了哪些事件实际上应该导致 UpdatePanel 内容被渲染。在上面的清单中,我们指示如果 ItemList 控件上触发了 SelectedIndexChanged 事件,则应更新 UpdatePanel 的内容。

您还需要将 ScriptManager 控件的属性 EnablePartialRendering 设置为 true

<atlas:ScriptManager ID="ScriptManager1" runat="server" 
       EnablePartialRendering="true">

EnablePartialRendering 设置为 true 可使 ScriptManager 控制页面的渲染,如稍后所述。服务器端 C# 代码如下:

protected void Page_Load(object sender, EventArgs e) {
    if (!IsPostback)
       ItemQuantityDisplay.Visible = false;
}

protected void ItemList_SelectedIndexChanged(object sender, EventArgs e) {
    ItemQuantityDisplay.Visible = true;

    try {
        ItemQuantityDisplay.InnerText =
              String.Format(" {0} in stock",
             Warehouse.GetItemQuantity(ItemList.SelectedValue));
    } catch (Exception ex) {
        Trace.Write("GetItemQuantity Error: " + ex.Message);
        ItemQuantityDisplay.InnerText = "Error retrieving quantity";
        ItemQuantityDisplay.Attributes["class"] = "Error";
    }
}

您可能已经注意到,该代码看起来像响应表单回发的典型 ASP.NET 服务器代码。但是,当您运行应用程序时,您会发现它的行为与其他 AJAX 示例类似,并且您没有编写一行 JavaScript。那么这一切是如何工作的呢?

发生的情况是,任何导致回发的客户端事件都被 Atlas 客户端框架拦截。然后,使用 JavaScript 通过 XMLHttpRequest 对象发布表单内容。从服务器的角度来看,这就像一次正常的回发,但从客户端的角度来看,表单并未提交,因为浏览器保持网页状态不变。然后,服务器端代码会扫描页面上的所有 UpdatePanel,并检查它们是否需要在客户端渲染或更新。这是通过检查触发器来完成的。只有 UpdatePanel 内部的页面部分(被触发更新)才会实际渲染,并且响应不是以 HTML 形式发送,而是以 XML 形式发送。客户端脚本然后解析响应 XML,提取并替换更新的 UpdatePanel 的内容。

为了在 UpdatePanel 更新时向用户显示进度消息,提供了名为 UpdateProgress 的服务器控件。当客户端向 Web 服务器发送更新请求时,UpdateProgress 服务器控件的内容会自动显示,并在更新完成后隐藏。

<atlas:UpdateProgress ID="ProgressDisplay" runat="server">
   <ProgressTemplate>
      <img src="images/loading.gif" alt="Loading" />
       Checking Stock...
    </ProgressTemplate>
</atlas:UpdateProgress>

当然,UpdatePanel 控件还有很多内容,它本身就需要一篇单独的文章。UpdatePanel 控件是 Atlas 最重要的功能,它将是使用 Atlas 的主要方式。我们已经在这里看到了一些 Atlas 的功能,现在,让我们研究一些其他框架。

其他 ASP.NET AJAX 框架

在 ASP.NET Atlas 开发之前,有几个开源实现用于在 ASP.NET 中提供 AJAX 支持。让我们快速浏览三个流行的开源实现。

Ajax.NET

由 Michael Schwartz 开发的 Ajax.NET 自豪地成为 ASP.NET 的第一个 AJAX 库。要使用 Ajax.NET,您需要从 AjaxPro.info 网站下载 AjaxPro 程序集,并将其放置在您网站的 *bin* 目录中。当使用页面方法时,服务器代码类似于 Atlas,只是属性名称发生变化:

void Page_Load(object sender, EventArgs e) {
   AjaxPro.Utility.RegisterTypeForAjax(GetType());
}

[AjaxPro.AjaxMethod]
public int GetItemQuantity(string itemID)  {
  return Warehouse.GetItemQuantity(itemID);
}

以下是如何从 JavaScript 调用此方法:

Intro.AjaxNetExample.GetItemQuantity(itemList.value, onCallbackComplete, 
                                                     onCallbackError);

Intro.AjaxNetExample 是 ASP.NET 页面的类名,在 @Page 声明中指定。

<%@ Page Language="C#" AutoEventWireup="true" 
         ClassName="Intro.AjaxNetExample"  %>

Ajax.Net 是一个轻量级库,它不支持 Atlas 的功能,如跨浏览器兼容性、服务器端控件等。但是,您可以将许多免费的 JavaScript 库用于其他 DHTML 工作。

Anthem.Net

Anthem.Net 由 Jason Diamond 开发,它通过服务器控件支持 ASP.NET 中的 AJAX。它有一组服务器控件,扩展了常规的 ASP.NET 服务器控件。为了使用 Anthem.net 启用我们的应用程序,我们必须用 Anthem.Net 的控件替换常规的 ASP.NET 控件。

<div>
  <label for="ItemList" accesskey="I">
    Items:</label>
  <anthem:ListBox runat="server" ID="ItemList" DataSourceID="ItemsSource" 
       DataTextField="ItemName"
       DataValueField="ItemID" EnableViewState="False" AutoCallBack="True" 
       OnSelectedIndexChanged="ItemList_SelectedIndexChanged">
  </anthem:ListBox>
  <anthem:Panel AutoUpdateAfterCallBack="true" runat="server">
  <div id="ItemQuantityDisplay" runat="server">
  </div>
  </anthem:Panel>
  <asp:SqlDataSource ID="ItemsSource" runat="server" 
      ConnectionString="<%$ ConnectionStrings:Items %>"
      SelectCommand="SELECT [ItemName], [ItemID] FROM [Items]">
   </asp:SqlDataSource>
</div>

因此,我们用 Anthem 的列表框替换了列表框。请注意,AutoCallback 属性设置为 true。这确保当列表框选择更改时,Anthem 框架将使用 XMLHttpRequest 发布表单内容。此回调与页面中 anthem:panel 控件的 AutoUpdateAfterCallback 属性结合使用。Anthem 框架查找更新的控件,并将其发送回客户端,客户端只需替换更新控件的 HTML。

Magic Ajax

我们将要研究的最后一个 AJAX 框架是 MagicAjax 框架。它最初是 Argiris Kirtzidis 的一篇 CodeProject 文章。在使用方面,MagicAjax 与使用 Atlas UpdatePanel 非常相似。您需要将要更新的控件和标记包含在 MagicAjax 的 AjaxPanel 类中。

<magicAjax:AjaxPanel runat="server" ID="MagicAjaxPanel">
<asp:ListBox runat="server" ID="ItemList" DataSourceID="ItemsSource" 
    DataTextField="ItemName"
    DataValueField="ItemID" EnableViewState="False" AutoPostBack="True" 
    OnSelectedIndexChanged="ItemList_SelectedIndexChanged">
</asp:ListBox>

<div id="ItemQuantityDisplay" runat="server">
</div>
</magicAjax:AjaxPanel>

服务器端代码与前面章节中示例的回发版本几乎相同。MagicAjax 不需要任何 JavaScript。

摘要

因此,我们已经看到了使用不同框架以不同方式实现样本的示例。您需要使用哪种框架完全取决于项目和您希望从框架获得的功能。Atlas 由 Microsoft 开发,是所有框架中功能最丰富的;但是,与另外三个框架相比,Atlas 相当重量级。MagicAjax.NET 和 Anthem.NET 非常适合轻量级和快速的 AJAX 实现,并提供完整的服务器控件支持。Ajax.NET 适用于使用 JavaScript 进行轻量级远程调用。ASP.NET 回调非常适合需要服务器控件 AJAX 功能的控件作者。

源代码包含我们讨论过的所有实现。一如既往,欢迎评论。

© . All rights reserved.