程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> ASP.NET >> 關於ASP.NET >> ASP.NET AJAX:UpdatePanel控件

ASP.NET AJAX:UpdatePanel控件

編輯:關於ASP.NET

不論好壞,UpdatePanel 控件都是 ASP.NET AJAX 社區所喜愛的。我說“好”,是因為 UpdatePanel 使部分頁面呈現變得相當簡單,而說“壞”,是因為它的簡便和易用性是以效率和令人啼笑皆非的帶寬為代價的。

UpdatePanel 可以為一般的網頁帶來 AJAX 神奇的好處,但是它不能提供我們與 AJAX 正常關聯的高效性。例如,您是否知道,當 UpdatePanel 控件對服務器執行異步 AJAX 回調以更新其內容時,這個請求包含了常規 ASP.NET 回發所包含的一切,其中還包括視圖狀態呢?大多數開發人員會以為 AJAX 取消了視圖狀態。而 UpdatePanel 的 AJAX 品牌卻並非如此。

如果您准備使用 UpdatePanel 控件,您需要清楚您在准備干什麼。在許多情況下,從性能的角度而言,應用程序最好是不使用 UpdatePanel,而是使用對 WebMethods 或頁面方法的異步調用。這樣做可能會大幅度降低網絡傳輸中的數據量。但是,它也是一個根本轉變,在這裡 UI 更新需要由開發人員使用該頁面上的 JavaScript 來進行顯式處理。

此外,ASP.NET AJAX 論壇上到處都有關於自定義 UpdatePanel 的提問。其實,只要您了解為 UpdatePanel 提供客戶端支持的 Microsoft® AJAX Library 中的 PageRequestManager、JavaScript 類,這裡面許多問題就可以迎刃而解。

既然已經提供了 ASP.NET AJAX,我想進一步檢查一下 UpdatePanel,從而進一步了解您可以如何對其進行自定義和優化,甚至在沒有它的情況下如何運行。這恰恰也是本期專欄包含的全部內容。

更新突出顯示

有時候您無法幫助 Microsoft 的開發人員,而只能對他們表示遺憾。如果他們不能將工作做得足夠好,就會遭到公眾的抨擊。然而,有時候他們的工作做得很出色,也會遭到抨擊。例如,我最近收到了一位客戶的電子郵件,抱怨 ASP.NET AJAX UpdatePanel 運行得有點過頭了。

UpdatePanel 使 ASP.NET 頁面回發到服務器,並將它變為流暢、無閃爍的更新時出現閃光和閃爍變得極其簡單。UpdatePanel 可以通過將回發轉換成異步回調(XML-HTTP 請求),以及使用客戶端上的 JavaScript 以刷新由 UpdatePanel 控件封裝的頁面的一部分來發揮它的魔力。閃光和閃爍會消失,因為浏覽器不會將該頁面重新繪制成與它在回發期間的一樣。

客戶的抱怨是,用戶有時候不會注意到頁面的那個部分已經更新了新內容。他的問題很簡單:ASP.NET AJAX 團隊的工作人員是否能使

updatePanel 閃爍再多一點,以便用戶不會錯過重要的更新?

不幸的是,ASP.NET AJAX 團隊可能對制造 UpdatePanel 閃爍沒什麼興趣。畢竟,消除閃爍才是發明 UpdatePanel 的初衷。但值得高興的是,您可以在浏覽器中使用 AJAX 的一些神奇功能,以吸引對已更新的 UpdatePanel 的注意。秘訣在於 Microsoft AJAX Library(由客戶端一半的 ASP.NET AJAX 組成的 JavaScript 類的庫)中的 Sys.WebForms.PageRequestManager 類。PageRequestManager 可以管理由 UpdatePanel 啟動的異步回調。它還負責在異步回調完成後更新 UpdatePanel 內的內容。

PageRequestManager 可以在更新前和更新後激發浏覽器中的事件。您可以將 JavaScript 中的這些事件關聯起來,並運行可以提醒用戶注意更新內容的代碼。關鍵事件被命名為 pageLoaded。此事件每次都會激發浏覽器中的頁面加載(它類似 Page_Load in ASP.NET)。它每次還會激發代表 UpdatePanel 控件完成而啟動的異步回調,並且會更新這個 UpdatePanel 中的內容。您可以使用兩行代碼(可以合並為一行)注冊 pageLoaded 事件的 JavaScript 處理程序:

var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_pageLoaded(pageLoaded);

第一行獲得對該頁面的 PageRequestManager 對象的引用。第二行注冊名為 pageLoaded 的 JavaScript 函數,作為 pageLoaded 事件的處理程序。

調用時,pageLoaded 事件處理程序會收到一個 Sys.WebForms.PageLoadedEventArgs 類型的參數,它是 Microsoft AJAX Library 中的另一個類。PageLoadedEventArgs 包含一個 get_panelsUpdated 方法,您可以調用該方法來枚舉所有的 UpdatePanel(如果有),其內容剛剛已更新。在默認情況下,UpdatePanel 就是客戶端上的 DIV,因此您可以使用 JavaScript 來使該 DIV 閃光,突出顯示它,或對它執行任何您想要的操作,以提醒用戶注意它。

圖 1 列出的代碼顯示了一個使用 pageLoaded 事件來執行更新突出顯示的方法。每次更新時,這個 JavaScript 都會使表示已更新的 UpdatePanel 的文檔對象模型 (DOM) 元素閃光,方法是使它們依次快速顯示和隱藏三次。閃光是通過名為 flashPanels 的幫助器函數來執行的,它將閃光次數作為輸入參數。

 Figure 1 閃光更新

<script type=”text/javascript”>

var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_pageLoaded(pageLoaded);

var _panels, _count;

function pageLoaded(sender, args)
{
    if (_panels != undefined && _panels.length > 0)
    {
        for (i=0; i < _panels.length; i++)
            _panels[i].dispose();
    }

    var panels = args.get_panelsUpdated();

    if (panels.length > 0)
    {
        _panels = new Array(panels.length);

        for (i=0; i < panels.length; i++)
            _panels[i] = new Sys.UI.Control(panels[i]);

        flashPanels(3);
    }
}

function flashPanels(count)
{
    _count = (count << 1) + 1;
       
    for (i=0; i < _panels.length; i++)
        _panels[i].set_visible(false);

    window.setTimeout(toggleVisibility, 50);
}

function toggleVisibility()
{
    for (i=0; i < _panels.length; i++)
        _panels[i].set_visible(!_panels[i].get_visible());
       
    if (--_count > 0)
        window.setTimeout(toggleVisibility, 50);
}
</script>

請注意,已更新的 UpdatePanel 的可見性可以通過切換打開和關閉來創建閃光效果。除了與 DOM 元素直接交互以外,代碼還會封裝代表具有 Sys.UI.Control 對象的 UpdatePanel 的 DOM 元素。然後,它使用 Sys.UI.Control 的 set_visible 和 get_visible 方法來切換可見性:

_panels[i].set_visible(!_panels[i].get_visible());

Sys.UI.Control 是在 Microsoft AJAX Library 中,具體而言是在 MicrosoftAjax.js 中找到的 JavaScript 類。以這種方式切換可見性的好處是,這是獨立於浏覽器的。這項操作在支持 ASP.NET AJAX 的每種浏覽器(幾乎就是所有的現代浏覽器)中同樣有效。另一方面,與浏覽器 DOM 直接交互的 JavaScript 代碼必須予以調整,以便在不同的浏覽器類型中使用。

取消 UpdatePanel 更新

pageLoaded 事件是 UpdatePanel 返回服務器更新其內容時,PageRequestManager 類激發的若干事件之一。PageRequestManager 激發的另一個重要事件是 initializeRequest,它在發生異步回調之前激發。

最近有人問我,是否有可能在運行時決定是否允許 AsyncPostBackTrigger 觸發一個 UpdatePanel 更新。回答是肯定的。這個操作通過處理 initializeRequest 事件來完成。

傳遞到 initializeRequest 處理程序的第二個參數是 initializeRequestEventArgs 類型的一個對象。這個對象包含 get_postBackElement 方法,它可以識別觸發更新的按鈕或其他元素。它還有一個您可用來在回調發生之前將其取消的 set_cancel 方法。下面是使用中的 set_cancel 方法的示例:

<script type=”text/javascript”>

var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_initializeRequest(initializeRequest);

function initializeRequest(sender, args)
{
    args.set_cancel(!confirm(‘Are you sure?’));
}
</script>

在這個示例中,intializeRequest 處理程序可以在回調執行之前彈出一個確認框,詢問用戶是否要繼續更新。單擊確認框中的“Cancel”(取消)可以將 true 傳遞給 set_cancel,這樣可以停止執行回調。在現實生活中,您可能覺得沒必要在允許繼續更新之前提示用戶確認,但如果要是能夠根據應用程序中其他地方的條件取消更新,它就可能很有用。

順便說一下,它還有可能在異步回調執行後但又尚未完成前取消它們。PageRequestManager 提供了 abortPostBack 方法來執行此操作;它還提供了 get_isInAsyncPostBack 方法來確定異步回調是否掛起。這些方法通常與 UpdateProgress 控件一起使用,以便顯示取消 UI。

多個 UpdatePanel

一個頁面可以承載幾個 UpdatePanel。默認情況下,當一個頁面上的 UpdatePanel 更新時,該頁面的其他 UpdatePanel 也會更新。有時候這是您想要的,但多半您並不需要每個 UpdatePanel 更新來響應其他 UpdatePanel。
通過將頁面上每個 UpdatePanel 控件的 UpdateMode 屬性設置為 Conditional,您可以選擇更新哪個 UpdatePanel 實例(及更新的時間)。然後,在 UpdatePanel 更新和調用服務端事件處理程序的時候,請調用您要更新的其他面板上的 UpdatePanel.Update。這樣可以通過減少呈現的控件的數量來減輕服務器的負載,而且它還減少了響應中的數據量,因為不進行更新的 UpdatePanel 不會將任何數據添加到響應中。

不使用 UpdatePanel 更新

AJAX 不僅可以創建更好的用戶體驗,它還可以提供更高效的網絡通信。當發生傳統的 ASP.NET 回發時,Web 窗體中包括視圖狀態在內的所有數據,都會傳輸到回發中的服務器。視圖狀態是 ASP.NET 頁面,尤其是使用 DataGrid 和 GridView 控件的 ASP.NET 頁面似乎會反應遲緩的一個原因。具有太多視圖狀態的頁面會降低性能,並且具有太多視圖狀態的頁面在 ASP.NET 應用程序中都太常見。

用 AJAX 回調替代 ASP.NET 回發的好處之一是能正確完成,AJAX 回調僅傳送需要被傳送的數據。這意味著,它們不必將視圖狀態包括在該傳輸中。

當您使用 UpdatePanel 在一個頁面上執行無閃爍更新時,您可能會認為您在進行高效構建。畢竟,UpdatePanel 使用的是 AJAX,不是嗎?不幸的是,如果您在 UpdatePanel 更新時檢驗一下網絡中的通信,您會發現您根本就沒有保存什麼東西,至少是在發送的時候沒有保存。通常在回發期間傳送到服務器的視圖狀態數據(與其他數據)也會在 UpdatePanel 回調期間傳送。事實上,來自 UpdatePanel 的異步 XML-HTTP 請求中所增長的數據幾乎與在標准 ASP .NET 回發中增長的數據相同。下面是有關 ASP.NET AJAX 不可告人的秘密:UpdatePanel 雖易於使用,但是通信效率不高。

幾乎沒有什麼辦法可讓您提高 UpdatePanel 的效率,但是您可以放棄使用 UpdatePanel,並轉而使用 ASP.NET AJAX 的其他功能來更新頁面內容,它不僅同樣流暢,而且更加高效。它只需要多一點點力氣,但是最後的結果往往讓人覺得是值得付出的,因為您可以大大降低在客戶端與服務器之間傳輸的數據量。

您還可以減少服務器上的負載。當 UpdatePanel 回調到服務器時,被回調定為目標的頁面會完成幾乎整個生命周期 — 該頁面會被實例化,該頁面中的控件也會被實例化,並且 UpdatePanel 內的控件也會完成一個正常呈現循環。那是更新該網頁一部分的大筆開銷。

作為示例,請考慮圖 2 中的頁面段。它提供了一個允許用戶鍵入郵政編碼的簡單 UI,並且單擊按鈕就可以用城市和州來初始化城市和州字段(請參見圖 3)。所有的控件都承載在 UpdatePanel 中,因此 Button 控件的回發被轉換為異步回調,並且事件處理程序 (GetCityAndState) 會被調用到該回調內部的服務器上。GetCityAndState(未顯示代碼)從郵政編碼文本框中讀取郵政編碼,將其轉換成城市和州,並相應地初始化表示城市和州的 TextBox 和 DropDownList。由於這些都發生在 UpdatePanel 內部,所以更新非常順暢,並且無閃爍。

 Figure 2 使用 UpdatePanel 填寫“城市”和“州”字段 

<asp:UpdatePanel ID=”UpdatePanel1” runat=”server”>
  <ContentTemplate>
    City:<br />
    <asp:TextBox ID=”City” runat=”server” />
    <br /><br />
    State:<br />
    <asp:DropDownList ID=”Region” runat=”server”>
        <asp:ListItem Value=”AL”>Alabama</asp:ListItem>
        <asp:ListItem Value=”AK”>Alaska</asp:ListItem>
        <asp:ListItem Value=”AZ”>Arizona</asp:ListItem>
          ...
        <asp:ListItem Value=”WV”>West Virginia</asp:ListItem>
        <asp:ListItem Value=”WI”>Wisconsin</asp:ListItem>
        <asp:ListItem Value=”WY”>Wyoming</asp:ListItem>
    </asp:DropDownList>
    <br /><br />
    Zip Code:<br />
    <asp:TextBox ID=”ZipCode” runat=”server” />&nbsp;
    <asp:Button ID=”AutofillButton” Text=”Autofill”
      OnClick=”GetCityAndState” runat=”server” />
  </ContentTemplate>
</asp:UpdatePanel>

 
圖 3 城市、州和郵政編碼 UI (單擊該圖像獲得較大視圖)

現在有一個問題。UpdatePanel 已使用這種方法改善了用戶的體驗,但是它並不能減少在網絡中傳輸的數據量。UpdatePanel 幾乎也不能減輕服務器上的負載,到 UpdatePanel 內部的控件呈現為止,在服務器上執行的處理幾乎與在完備回發期間所發生的一樣。它必須是這種方法,因為 UpdatePanel 控件的好處之一是服務器端的事件處理程序(如 GetCityAndState)在異步回調內執行的操作與在傳統回發中所執行的操作一樣。這意味著該頁面上的控件必須被實例化,它們必須有權訪問視圖狀態等。

圖 4 說明了如何在不使用 UpdatePanel 控件的情況下實現相同功能。這次,Autofill 按鈕被綁定到一個 JavaScript,這個 JavaScript 激發了異步 XML-HTTP 請求給名為 GetCityAndState 的 ASMX Web 方法。該調用是通過名為 ZipCodeService 的 JavaScript 代理置入的,它由 ScriptManager 控件中的服務引用生成。GetCityAndState 以 ZIP Code 字符串作為輸入,並返回一個包含相應的城市和州兩個項的字符串數組。完成函數 onGetCityAndStateCompleted 會檢索該數組中的項,並將它們插入到城市和州字段。從外部看起來結果是一樣,但是它在內部的運行方式卻是迥異。

 Figure 4 不使用 UpdatePanel 填寫“城市”和“州”字段

<asp:ScriptManager ID=”ScriptManager1” runat=”server”>
    <Services>
        <asp:ServiceReference Path=”ZipCodeService.asmx” />
    </Services>
    <Scripts>
      <asp:ScriptReference Name=”PreviewScript.js”
Assembly=”Microsoft.Web.Preview” />
    </Scripts>
</asp:ScriptManager>

City:<br />
<asp:TextBox ID=”City” runat=”server” />
<br /><br />
State:<br />
<asp:DropDownList ID=”Region” runat=”server”>
    <asp:ListItem Value=”AL”>Alabama</asp:ListItem>
    <asp:ListItem Value=”AK”>Alaska</asp:ListItem>
    <asp:ListItem Value=”AZ”>Arizona</asp:ListItem>
      ...
    <asp:ListItem Value=”WV”>West Virginia</asp:ListItem>
    <asp:ListItem Value=”WI”>Wisconsin</asp:ListItem>
    <asp:ListItem Value=”WY”>Wyoming</asp:ListItem>
</asp:DropDownList>
<br /><br />
Zip Code:<br />
<asp:TextBox ID=”ZipCode” runat=”server” />&nbsp;
<asp:Button ID=”AutofillButton” Text=”Autofill”
  OnClientClick=”autoFill(); return false;” runat=”server” />

<script type=”text/javascript”>
function autoFill()
{
    var tb = new Sys.Preview.UI.TextBox ($get(‘ZipCode’));
    var zip = tb.get_text();

    if (zip.length == 5)
        ZipCodeService.GetCityAndState (zip,
            onGetCityAndStateCompleted);
}

function onGetCityAndStateCompleted(result)
{
    if (result != null)
    {
        var tb = new Sys.Preview.UI.TextBox ($get(‘City’));
        tb.set_text(result[0]);

        var select =
            new Sys.Preview.UI.Selector ($get(‘Region’));
        select.set_selectedValue(result[1]);
    }
}
</script>

以下說明了如何通過 JavaScript 代理實現調用 ASMX Web 方法:
 復制代碼
[ScriptService]
public class ZipCodeService : System.Web.Services.WebService
{
    [WebMethod]
    public string[] GetCityAndState(string zip)
    {
      ...
    }
}

除了它所屬的類的屬性是 ScriptService,而非 WebService 這一點以外,這是一個從各方面來看都可稱為標准的 Web 方法。雖然 ScriptService 與 WebService 具有相同的意義,但是它還包含了附加的意義,那就是 Web 服務的 WebMethods 可以從客戶端腳本調用。

除了允許常規 WebMethods 充當 XML-HTTP 請求的目標之外,ASP.NET AJAX 還支持一種特殊類別的 Web 方法,稱為頁面方法。頁面方法是在 Web 頁面中實現的 WebMethods,也就是在 ASPX 文件或 codebehind 文件中,而不是在 ASMX 文件中。Page 方法允許開發人員在不構建專業 Web 服務的情況下提供用於 XML-HTTP 回調的端點。

頁面方法必須是公開的靜態方法,並且和 WebMethods 一樣,必須具有 WebMethod 屬性。(WebMethods 和頁面方法還可以具有 ScriptMethod 屬性,該屬性可提供退出網絡的附加控件。)在客戶端,頁面方法可以通過特殊的 PageMethods 代理從 JavaScript 調用。
與 Web 服務不同,頁面方法無需服務引用。但是,您必須通過將 ScriptManager 控件的 EnablePageMethods 屬性設置為 true 來啟用頁面方法,如下所示:

<asp:ScriptManager ID=”ScriptManager1” runat=”server”
  EnablePageMethods=”true” />

實質上,頁面方法提供了與 WebMethods 一樣的高效性。當調用頁面方法時,視圖狀態和其他輸入未被傳送到服務器。並且既然頁面方法都是靜態的,那麼就可以在沒有實例化頁面對象的情況下調用它們。對頁面方法的調用不會調用由常規 ASP.NET 請求觸發的頁面生命周期。

Web Service != SOAP 和 XML

ASP.NET AJAX 最重要的功能之一是可以調用使用浏覽器客戶端的異步 XML-HTTP 請求的服務器上的 WebMethods 和頁面方法。但是當我告訴別人這個功能的時候,我不禁感覺有點畏縮。

我們大多數人聽到“Web 服務”這個術語時,都會想到 SOAP 和 XML。為了語言簡練,通常不會在同一個句中同時提到這兩種技術。是的,您可以使用 ASP.NET AJAX 從 JavaScript 中調用 WebMethods。但是您錯了,ASP.NET AJAX 並不使用 SOAP 和 XML。

圖 5 顯示了當執行圖 4 中的 Web 方法調用時在網絡中傳輸的數據。除了 HTTP 標頭外,請求中被傳輸的唯一數據是用戶鍵入的 ZIP Code,而響應中返回的唯一數據是一對表示城市和州的字符串。您不會看到任何 SOAP 或 XML(或視圖狀態,就此而言)。相反,輸入和輸出會通過使用 JavaScript Object Notation (JSON) 進行編碼,它比 XML 簡短得多,也更易處理。請求和響應並不使用 SOAP,而是使用一個簡單的協議,而這個協議基本上就是 HTTP。HTTP 和 JSON 的組合使對 WebMethods 和頁面方法的 ASP.NET AJAX 調用比傳統的 Web 服務調用要有效得多。

 Figure 5 JSON 編碼的輸入和輸出

請求 
 
POST /Ajax/ZipCodeService.asmx/GetCityAndState HTTP/1.1
Accept: */*
Accept-Language: en-us
Referer: http://localhost:1997/Ajax/ZipCodePage.aspx
UA-CPU: x86
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; ...)
Host: localhost:1997
Content-Length: 15
Connection: Keep-Alive
Cache-Control: no-cache

{"zip":"98052"}

響應 
 
HTTP/1.1 200 OK
Server: ASP.NET Development Server/8.0.0.0
Date: Fri, 29 Dec 2006 21:06:17 GMT
X-AspNet-Version: 2.0.50727
Cache-Control: private, max-age=0
Content-Type: application/json; charset=utf-8
Content-Length: 16
Connection: Close

{"REDMOND", "WA"}

JSON 是一個正在嶄露頭角的行業標准序列化格式。它還是被 ASP.NET AJAX 使用的本機格式。Microsoft AJAX Library 的 Sys.Serialization.JavaScriptSerializer 類支持在客戶端進行 JSON 序列化和反序列化。System.Web.Script.Serialization.JavaScriptSerializer 類支持在服務器上進行 JSON 序列化和反序列化。

並非所有類型均與 JSON 兼容。例如,JSON 不能處理具有循環引用的對象。當您需要返回不能與 JSON 兼容的復雜數據類型時,其實您可以使用 ASP.NET AJAX 的 ScriptMethod 屬性將返回類型序列化為 XML。這個技術對返回 XML 數據的方法也很有用,如下所示:

[ScriptMethod (ResponseFormat=ResponseFormat.Xml)]
public XmlDocument GetData()
{
  ...
}

此外,您還可以構建和注冊自定義 JSON 轉換器,它允許將通常不能與 JSON 兼容的類型序列化和反序列化。ASP.NET AJAX January Futures CTP 包含三個這樣的轉換器:一個針對 DataSet,一個針對 DataTable,還有一個針對 DataRow。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved