The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

如何動態修改巢狀 MasterPage 的 MasterPageFile 屬性

我前天遇到一個 ASP.NET 開發的問題,我有一個網站,其中有些頁面套用了一層的 MasterPage、有些套用了兩層 MasterPage、有些套用了三層 MasterPage,這些套兩層以上 MasterPage 的 MasterPage 在第二層的地方都是套用第一層的 MasterPage,不過客戶突然說第一層的 MasterPage 必須要依據不同的網域名稱(Domain Name)而選用不同的 MasterPage,最後耗了我三個小時才解決這個問題。

MasterPage 的套用關係大致的示意如下:

MasterPageTop.master
   ->  MasterPageChannel1.master
       -> MasterPageChannel1_1.master
           -> ContentPage.aspx

而我的需求是要動態變更 MasterPageChannel1.master 裡的 MasterPageFile 屬性,讓 ContentPage.aspx 在顯示的時候將 MasterPageTop.master 改成 MasterPageTop2.master。

問題與難點描述

一般來說,若 ContentPage.aspx 只有套一層 MasterPage 的話,只要在該頁面的 Page_PreInit 事件中指定 this.MasterPageFile 屬性即可動態變更該頁的 MasterPage,如下範例:

protected void Page_PreInit(object sender, EventArgs e)
{
    if (Request.Url.Host == "www.mydomain.com")
    {
        this.MasterPageFile = "~/MasterPageTop2.master";
    }
}

因為我有些頁面的 MasterPage 是套多層的,所以我就嘗試在 MasterPageChannel1.master 去設定 Page_PreInit 事件設定相同的程式碼,不過好像沒效果,程式完全沒有執行,經查詢後才得知原來 MasterPage 裡面並無 Page_PreInit 事件可以設定,最早的事件就是 OnInit 事件而已,所以從 MasterPage 去設定 MasterPageFile  是不可行的。

當下已經晚上九點,又還沒吃飯,感覺又好像是個無解的難題,不過總覺得不甘心就這樣放棄回家,一直拼到快 11 點才讓我寫出完美的解決方法。

動態修改巢狀 MasterPage 的 MasterPageFile 屬性的解決方案

既然從 MasterPage 去設定 MasterPageFile  是不可行的,那麼唯一個可能性就只有從內容頁面(ContentPage.aspx)下手了,因為所有前台 aspx 網頁都需要做出判斷,所以我寫了一個 BasePage,並覆寫(override) OnPreInit 事件,去動態修改頁面與每一層 MasterPage 中的 MasterPageFile 屬性(最多三層),而我的第一版 BasePage 寫成這樣:

/// <summary>
/// 讓所有前端的頁面繼承的共用 Page 類別
/// </summary>
public class FrontBasePage : Page
{
    protected override void OnPreInit(EventArgs e)
    {
        if (Request.Url.Host == "www.mydomain.com")
        {
            if (this.MasterPageFile == "/MasterPageTop.master")
                this.MasterPageFile = "~/MasterPageTop2.master"; 

            if (Master != null)
            {
                if (Master.MasterPageFile == "/MasterPageTop.master")
                    Master.MasterPageFile = "~/MasterPageTop2.master"; 

                if (Master.Master != null)
                {
                    if (Master.Master.MasterPageFile == "/MasterPageTop.master")
                        Master.Master.MasterPageFile = "~/MasterPageTop2.master"; 

                    if (Master.Master.Master != null)
                    {
                        if (Master.Master.Master.MasterPageFile == "/MasterPageTop.master")
                            Master.Master.Master.MasterPageFile = "~/MasterPageTop2.master";
                    }
                }
            }
        }
        // 若有在內容頁面中有定義 Page_PreInit 事件的話會先在 base.OnPreInit(e) 執行!
        base.OnPreInit(e); 
    }
}

其實這樣寫已經可以了,不過在我的頁面中卻無法正確的替換掉正確的 MasterPageTop2.master,原因就出在 base.OnPreInit(e); 這行。

若在內容頁面(Content Page)中有定義 Page_PreInit 事件的話會,真正呼叫這個事件執行的地方是在 base.OnPreInit(e) 內,而好巧不巧的我其中有一頁的 Page_PreInit 事件剛好也有寫到指定 MasterPageFile 屬性的程式,導致我在 FrontBasePage 所做出的設定到了 base.OnPreInit(e) 又被改回來了。

所以我最後我將 base.OnPreInit(e) 移到上面後,所有問題就都修復了!修改後的程式碼如下:

/// <summary>
/// 讓所有前端的頁面繼承的共用 Page 類別
/// </summary>
public class FrontBasePage : Page
{
    protected override void OnPreInit(EventArgs e)
    {
        // 若有在內容頁面中有定義 Page_PreInit 事件的話會先在 base.OnPreInit(e) 執行!
        base.OnPreInit(e); 

        if (Request.Url.Host == "www.mydomain.com")
        {
            if (this.MasterPageFile == "/MasterPageTop.master")
                this.MasterPageFile = "~/MasterPageTop2.master"; 

            if (Master != null)
            {
                if (Master.MasterPageFile == "/MasterPageTop.master")
                    Master.MasterPageFile = "~/MasterPageTop2.master"; 

                if (Master.Master != null)
                {
                    if (Master.Master.MasterPageFile == "/MasterPageTop.master")
                        Master.Master.MasterPageFile = "~/MasterPageTop2.master"; 

                    if (Master.Master.Master != null)
                    {
                        if (Master.Master.Master.MasterPageFile == "/MasterPageTop.master")
                            Master.Master.Master.MasterPageFile = "~/MasterPageTop2.master";
                    }
                }
            }
        }
    }
}

參考連結