[ASP.NET]回上一頁的處理

  • 53071
  • 0

[ASP.NET]回上一頁問題

前言

這次要簡單介紹的是到處看得到的回上一頁,
回上一頁最基本的就是javascript的history.back(),動作就如同Browser上的回前頁。
history.back()跟瀏覽器的回上一頁,特點是上一頁輸入的值都會仍然存在,這是瀏覽器自己的暫存。

另外的回上一頁需求,可能是需要回到上一頁的網址,並且重新初始化,
這種需求作法可能就是透過javascript的location.href=document.referrer,
或是Server端的Response.Redirect(Request.UrlReferrer)來達到「導頁到上一頁」。

更進階的需求,還有回前頁時,要保留前前頁傳過來的參數(可以用QueryString的還好解決,有Url就解決,其他參數就比較麻煩)。
回前頁時,除了要保留前前頁傳到前頁的參數,還需要重新初始化前頁。
前頁 還要能回到前前頁等等需求…參數都是先前傳的,但是結果都是最新的資料…
也有一些頁面入口是不只單一入口,傳遞的參數也都不同等等…
這種可能就要搭配session或sitemap等方式來解決。

上面這種導頁式的需求,這篇文章先不介紹。

今天要介紹的,只是AJAX.NET、ASP.NET跟history.back(),因為postback可能引發的問題。
 

問題

ASP.NET特有的Postback是一切Event-driven的起點,
但是Postback的原理,等於是Post form到自己這一頁,
所以對Browser來說,理所當然的會認為這兩頁是不同頁,因為Post就是為了修改Server端裡的狀態。(與Get相比的話啦)
所以回上一頁,當然就是回到Postback前的那個動作。

這樣子對User來說,當然會覺得為什麼我按回上一頁,沒有回到前一個網頁,而是在同一頁,只是變成沒按按鈕前的畫面?
其他網頁或是以前ASP的網頁,都不會這樣啊?
當然囉,因為其他技術沒有這詭異的Postback,Http本來也就沒有這種東西,
這一切攏系阿共啊 ASP.NETㄟ陰謀啦…因為ASP.NET要模擬出跟Window form一樣的事件,
透過eventTarget跟eventArgument和.net framework裡的javascript function  __dopostback(),在http上達到這樣的效果。

所以這邊我們就用點小技巧,來解決Postback造成的問題。

 

Solution

其實這問題的根本原因,就如同上一段所說,
Postback對Browser來說,本來就是兩個頁面剛好是同一個網址而已,
以頁面A到頁面B,然後頁面B postback來說,
我們用這樣來表示A->B->B1->B2……我們希望不管B幾,按了回上一頁,都可以回到A的原始狀態。

那很簡單,我們只要記住Postback了幾次,
將history.back(),改成history.go()即可。
如果history.go(-1),就代表是原本的history.back()。

有了以上的觀念後,其實Postback的問題就迎刃而解了,
但是!還有一個詭異的東西,是AJAX.NET裡面,很特殊的東西叫做UpdatePanel,
在AJAX.NET裡,在UpdatePanel裡的Postback動作,會被攔截並轉成非同步的xmlhttprequest,
但是對頁面的life cycle來說,卻是與一般Postback沒有兩樣。

而透過UpdatePanel轉發的postback,對history的影響呢?
由於不是一般的Postback,所以Browser的history並不會被影響,
所以我們在設計history.go(n)的時候,要把這一點也考量進去。

好,有了以上的考量後,我們來包裝我們的回上一頁吧。
 

Play it

我們方便起見,就做在MasterPage裡面,這樣所有套用MasterPage的頁面都可以有這樣的功能。

只需要在我們的MasterPage上加入ScriptManager:


<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>未命名頁面</title>
    <asp:ContentPlaceHolder id="head" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <asp:ScriptManager ID="ScriptManager1" runat="server">
        </asp:ScriptManager>
        <asp:ContentPlaceHolder id="ContentPlaceHolder1" runat="server">       
        </asp:ContentPlaceHolder>        
    </div>
    </form>
</body>
</html>


接著在.master裡面,在Page_PreRender去進行我們的設計,為什麼要在這?
其實放在Page_Load也是可以的,
但是放在Page_PreRender,可以避免Control Event導頁到別的地方時,無謂的多跑了這段的處理。


    protected void Page_PreRender(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            this.ViewState["postBackTimes"] = -1;
        }
        else
        {
            if (!this.ScriptManager1.IsInAsyncPostBack)
            { this.ViewState["postBackTimes"] = Convert.ToInt16(this.ViewState["postBackTimes"]) - 1; }

        }

        string gobackjs = @"function MyBack()
                            {history.go(" + Convert.ToString(this.ViewState["postBackTimes"]) + @");}";
        
        ScriptManager.RegisterClientScriptBlock(this.Page, this.Page.GetType(), "back", gobackjs, true);
    }

 

當PostBack為false時,我們把history.go(N) 的N預設為-1。
當PostBack時,我們要判斷是一般的PostBack,還是UpdatePanel所轉發的AsyncPostBack,
只針對一般的PostBack去作-1的動作,因為一般的PostBack才會影響history。

接著動態的註冊一段javascript,每次PostBack都要Render新的history.go(N)的N。

這樣就搞定了,原本頁面上要呼叫history.back()的功能,只要呼叫MyBack()即可。

套用MasterPage的所有頁面都不需要考慮Postback或有無UpdatePanel的問題,
也不用重新註冊一段javascript,
也不用擔心要傳什麼參數,直接呼叫MyBack()就會回到瀏覽器history裡面,上個頁面的網址。


Sample Code:backPreviousMasterPage.rar


blog 與課程更新內容,請前往新站位置:http://tdd.best/