官术网_书友最值得收藏!

  • 決戰.NET
  • 黃忠成
  • 1686字
  • 2018-12-27 16:56:04

5.3 Single Page架構

自從筆者于課堂上教導學員如何使用MasterPage、UpdatePanel等技術來撰寫網頁后,學員常問的問題莫過于樣板設計與異步刷新如何結合,學員們常常問道:“老師,我們的主網頁上右方有一個TreeView,用來切換要使用的功能頁面,功能頁面充分地利用了UpdatePanel控件,所以操作上并不會產生過多的刷新動作,不過現在仍有一個問題,那就是TreeView于切換功能頁面時,仍會因頁面的切換而產生刷新動作,能否連這個都避免掉?”呃!聽到這種問題,筆者也只能笑著說:“頁面切換時的閃爍是網頁程序的必要之惡,要連這點都避免,可不是寫幾百行程序代碼可以解決的,這是系統架構的問題了!”那此問題是否真的無解呢?未必!筆者回答的最后一段話是:這是系統架構的問題,而不是無解,因為只要動動腦筋,想想UpdatePanel、MasterPage,以及UpdatePanel動態加載UserControl等上面提過的技術,便能規劃出一個特殊的網頁程序架構,稱為Single Page架構(圖5-7)。在此架構中,系統中只有一個.aspx,套用了某個MasterPage,其余的功能頁面均以UserControl存在,當切換功能頁面時,事實上是動態加載了一個UserControl至UpdatePanel中,這樣一來不會有頁面切換,自然也就沒有網頁刷新的問題了。

那該如何實現這個架構呢?請照著以下步驟做。

1. 新增一個MasterPage,命名為DefaultFace.master。

2. 放入一個ScriptManager控件。

3. 建立一個單列兩欄的TABLE。

4. 將默認的ContentPlaceHolder1放到第二欄中。

5. 于第一欄放入一個TreeView控件,命名為TreeView1。

true

圖5-7

6. 于TreeView1中新增兩個Node,分別是Customers及Products,如程序5-8所示。

7. 建立一個新網頁,命名為DefaultFace.aspx,套用DefaultFace.master。

8. 于ContentPlaceHolder1中放入一個UpdatePanel控件,命名為UpdatePanel1,將UpdateMode設為Conditional。

9. 切換至DefaultFace.aspx.cs,鍵入程序5-9的程序代碼。

程序5-8

    Samples\5\AdvAjaxDemo\DefaultFace.master
    <%@ Master Language="C#" AutoEventWireup="true"
      CodeFile="DefaultFace.master.cs" Inherits="DefaultFace" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <asp:ScriptManager ID="ScriptManager1" runat="server">
            </asp:ScriptManager>
            <table>
            <tr>
            <td valign=top>
                <asp:TreeView ID="TreeView1" runat="server" ImageSet="BulletedList"
                      ShowExpandCollapse="False">
                    <ParentNodeStyle Font-Bold="False" />
                    <HoverNodeStyle Font-Underline="True" ForeColor="#5555DD" />
                    <SelectedNodeStyle Font-Underline="True" ForeColor="#5555DD"
                      HorizontalPadding="0px"
                      VerticalPadding="0px" />
                    <Nodes>
                      <asp:TreeNode Text="基本數據" Value="None">
                        <asp:TreeNode Text="客戶數據"
                            Value="Faces/CustomersControl.ascx"></asp:TreeNode>
                        <asp:TreeNode Text="產品數據"
                            Value="Faces/ProductsControl.ascx"></asp:TreeNode>
                      </asp:TreeNode>
                    </Nodes>
                    <NodeStyle Font-Names="Verdana" Font-Size="8pt"
                    ForeColor="Black" HorizontalPadding="0px"
                    NodeSpacing="0px" VerticalPadding="0px" />
                </asp:TreeView>
            </td>
            <td valign=top>
            <asp:contentplaceholder id="ContentPlaceHolder1" runat="server">
            </asp:contentplaceholder>
            </td>
            </tr>
            </table>
        </div>
        </form>
    </body>
    </html>

程序5-9

    Samples\5\AdvAjaxDemo\DefaultFace.master.cs
    using System;
    using System.Data;
    using System.Configuration;
    using System.Collections;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    public partial class DefaultFace : System.Web.UI.Page
    {
        private void DetectControlLoad()
        {
            string loadKey = SearchParams("Dynamic_UserControl_Hidden1");
            if (loadKey != null)
            {
                string controlLoaded = loadKey;
                LoadUserControl(controlLoaded);
            }
        }
        private string SearchParams(string paramName)
        {
            foreach (string key in Request.Params.Keys)
            {
                if (key != null && key.Contains(paramName))
                    return Request.Params[key];
            }
            return null;
        }
        private void LoadUserControl(string path)
        {
            UpdatePanel1.ContentTemplateContainer.Controls.Clear();
            HtmlInputHidden hidden1 = new HtmlInputHidden();
            hidden1.Value = path;
            Control c = LoadControl(path);
            if (Cache[path] != null)
                c.ID = (string)Cache[path];
            else
            {
                c.ID = "Dynamic_" + Guid.NewGuid().ToString().Replace('-', '_');
                Cache[path] = c.ID;
            }
            hidden1.ID = "Dynamic_UserControl_Hidden1";
            c.Controls.Add(hidden1);
            UpdatePanel1.ContentTemplateContainer.Controls.Add(c);
        }
        private void AttachTrigger()
        {
            TreeView tv = (TreeView)Master.FindControl("TreeView1");
            AsyncPostbackTrigger trigger = new AsyncPostbackTrigger();
            trigger.ControlID = tv.ID;
            trigger.EventName = "SelectedNodeChanged";
            UpdatePanel1.Triggers.Add(trigger);
        }
        protected void Page_Init(object sender, EventArgs e)
        {
            AttachTrigger();
            if (IsPostback && !Request.Params["__EVENTTARGET"].EndsWith(
              "TreeView1"))
            {
                DetectControlLoad();
            }
        }
        protected void Page_Load(object sender, EventArgs e)
        {
            if (IsPostback && ScriptManager.GetCurrent(
              this).AsyncPostbackSourceElementID.EndsWith("TreeView1"))
            {
                TreeView tv = (TreeView)Master.FindControl("TreeView1");
                if(tv.SelectedValue != "None")
                    LoadUserControl(tv.SelectedValue);
            }
            else if (!IsPostback && Request.QueryString["Page"] != null)
                LoadUserControl("Faces/"+Request.QueryString["Page"] + ".ascx");
        }
    }

程序5-9頗不容易理解,讓筆者針對其中的幾個關鍵函數做一些說明。首先是AttachTrigger函數,此函數于Page_Init函數中被調用,用途與前例相同,將MasterPage中的TreeView設定為本頁面中UpdatePanel1的Trigger,這樣只要TreeView產生Async-Postback動作,本頁面中的UpdatePanel1就被視為是需要刷新的UpdatePanel。在Page_Init函數中,若狀態為Postback,且觸發Postback的控件不是TreeView1的話,就代表網頁上已經有UserControl被加載了,此時必須進行UserControl的Reload動作,只有這樣才能維持UserControl的正常運作。當狀態處于Postback,且是Async-Postback時,Page_Load會判斷觸發的是否為TreeView1,是的話就代表必須進行UserControl的加載動作,此時將通過TreeView的SelectedValue來取得要加載的UserControl URL,然后進行加載動作。加載動作由LoadUserControl函數來運行,此處會先加載指定的UserControl,接著于該UserControl中插入一個Hidden Field,用途是記錄目前頁面上已載入的UserControl,有這個Hidden Field的存在,DetectControlLoad函數才能決定要重載哪一個UserControl。這里有一個相當特殊的設計,UserControl的ID是結合GUID產生的,不同的UserControl將對應著不同的GUID,此設計是應對當Async-Postback發生時,ASP.NET仍會由ViewState中讀出上次UserControl所存放的值,若不同的UserControl使用同樣的ID,可能會因此造成錯誤。在完成主要頁面的設計后,接著是設計功能頁面,也就是動態加載的UserControl,本例中將使用Northwind數據庫中的Customers、Products數據表為數據源,分別依上節的無刷新編輯模式撰寫CustomersControl.ascx及ProductsControl.ascx,由于這兩個頁面的建立方式與上節大致相同,此處就不再多言,直接看結果,如圖5-8所示。

true

圖5-8

這個范例不只因UserControl內部使用Async-Postback而不會有網頁閃爍情況發生,連帶著于切換時也因使用了Async-Postback而不發生網頁閃爍。在你對此架構感到滿意,立即想套用前,請先了解一點,這個架構的優點同時也是其缺點,因為沒有頁面的切換動作,功能頁面間的參數傳遞將無法循QueryString方式完成,而必須改由Session、Cache等其他途徑完成。也因為此點,當UserControl中以ScriptManagerProxy來含入外部的JavaScript文件時,這些JavaScript文件會一直存在于瀏覽器的內存中,即使切換到其他UserControl也不會移除,這點在套用此架構時要特別注意。另外,當用戶點擊瀏覽器的重新整理后,此架構不會停留在同一個功能頁面,而是回到最初的頁面。只有當這些問題可以被接受時,套用此架構才會有意義。另外,當需要從URL直接跳往某一頁面時,只需指定Page參數值為UserControl的名稱即可,例如于URL上直接鍵入:http://<your web server>/AdvAjaxDemo/DefaultFace.aspx?Page=CustomersControl,將會直接跳往客戶數據的功能頁面,這在需要以聯結方式跳往特定功能頁面時相當有用。

主站蜘蛛池模板: 麦盖提县| 高雄市| 阜城县| 富蕴县| 昆山市| 长兴县| 乡宁县| 比如县| 漯河市| 呼玛县| 革吉县| 周口市| 太湖县| 娄烦县| 南召县| 西藏| 两当县| 西平县| 拉萨市| 崇仁县| 佛坪县| 崇明县| 玉屏| 辉县市| 永寿县| 邯郸市| 盐城市| 岳阳市| 平安县| 城步| 苏尼特右旗| 临桂县| 日土县| 揭西县| 深圳市| 巩留县| 海兴县| 松桃| 安新县| 左贡县| 冷水江市|