ASP.Net動態切換主題
參考課堂講解改編,大部分借用了老師的原話
Step1:編寫一個主頁面
可以參考下面的佈局設計,在這裏的附件中提供了一個示例主界面,在MasterPage.Master中。
Step 2:編寫主頁面的佈局
在aspx文件裏面編寫自己的佈局,並使用class來添加樣式。因爲使用主題,暫時不用引入CSS文件。參考文件TestMaster.aspx。
Step 3:編寫主題
- 每一個主題對應一個文件夾,主題的名字就是文件夾名字,主題文件夾裏放置 CSS 文件和其它資源文件。建議建立子文件夾,分類存放圖片、聲音、視頻等文件。
- 每一個主題都有一個對應的 CSS 文件,該文件應當與主題名稱一致,並且放到主題文件夾下面(別放到子文件夾裏)。
- 在主題 CSS 文件裏給主頁面中各個 class 添加樣式。不同主題所面對的 class 是一樣的,只不過每一個 class 的樣式不一樣。
注:參考附件的主題編寫,自己可以自行編寫,設計不同的主題。
Step 4:添加動態切換主題的控件
- 在主頁面的合適位置,添加一個 DropDownList,用它來提供各個主題的選擇項。並且編寫它的事件處理方法。
- 請再移動到 selectTheme_SelectedIndexChanged 這裏,閱讀該方法的實現。
幫助:鼠標右鍵點擊下面的 selectTheme_SelectedIndexChanged,選擇 查看代碼,即可跳轉。
Step 5:編寫 DropDownList 的事件響應方法
根據 ASP.NET 頁面生命週期,當用戶改變 DropDownList 切換主題的時候,該方法被調用,然而此時已經錯過了能夠設置主題的時機:PreInit。所以,這裏只能將用戶選擇的主題先保存起來,再讓頁面重新加載一次。在下一次加載的時候,讀取保存起來的主題名稱,再切換主題。在這裏,我們選擇使用 Cookie 來保存用戶的選擇。
參考代碼:(這裏包含註釋,附件的代碼註釋少)
Step 6:創建一個自定義 Page 類
-
爲什麼需要這個類:
設置主題 Theme 或者 StyleSheetTheme,都必須在 System.Web.UI.Page 的子類裏進行。但是主頁面的父類是 System.Web.UI.MasterPage,沒法設置。而在每一個我們編寫的頁面裏都去重複寫設置主題的代碼,顯然是重複勞動,跟主頁面一次搞定重複工作的思想也不一致。所以,可以藉助繼承,先在System.Web.UI.Page 下面寫一個新的頁面類,讓它去配合主頁面的工作,負責主題的動態切換。之後我們具體的aspx頁面,再繼承這個類就好了。 -
怎麼添加這個類?
如果創建的是 ASP.NET 網站,那麼需要先創建一個 APP_Code 文件夾,在這個文件夾裏創建類。注意,APP_Code 是 ASP.NET 的特殊用途文件夾,名字固定,更改後失效。只有在這個文件夾裏創建類,才能夠在其它 cs 文件裏引用它。
如果創建的是 Web App 工程,那麼可以在工程根目錄或者一個子文件夾裏創建一個類。但是特別注意,不能在APP_Code 文件夾裏創建類,會直接報錯。因爲 APP_Code 是專門爲 ASP.NET 網站預留的,在 Web App 工程裏不需要,而且還起反作用。最坑爹的是,VS 居然有提供新建 APP_Code 文件夾的選項,搞的和真的是的。 -
這個類要怎麼寫:
(1)首先給它添加一個父類,必須是 System.Web.UI.Page;然後給它添加一個 public 無參構造函數。構造函數裏什麼也不要寫。注意,一定不要忘記 public 修飾,否則該構造函數就是 private 的,將沒法直接用 new 去實例化它,會報錯。
(2)之後,根據我們設置主題的方式,添加如下方法:
1、如果要設置 Theme 屬性,讓主題後生效,那麼要添加 Page_PreInit 方法,響應 PreInit 事件。相關代碼 請閱讀下面的 Step 7A。
Step 7A:
(a). 這是一個事件處理方法,返回值必須是 void,兩個參數的類型分別是 object 和 EventArgs,且次序不能顛倒。這個方法的名字是 Page_PreInit,不能亂改。它是由 Page 的 AutoEventWireup屬性決定的。該方法不能是 private 修飾。
( b). 由於 PreInit 事件觸發得非常早,我們甚至連控件也看不到。在這裏唯一可以獲取的就是用戶的 http請求數據,而且還是原始數據。由於 Cookies 也是隨着 http 數據包一起發送的,所以之前寫入到Cookies 的主題名字還是在這裏能夠找到的。我們要做的就是取出之前存儲的主題名字,然後根據這個名字設置主題。注意,Cookies 數據是可以被 篡改的,一定要做充足的檢查。
參考代碼如下:
2、如果要設置 StyleSheetTheme 屬性,讓主題先生效,那麼要重寫 StyleSheetTheme 的 get 方法。相關代碼請閱讀下面的 Step 7B。
(a)StyleSheetTheme 屬性比較奇特,你會發現它無法被設置(沒有 set 方法)。所以正確的處理方式是重載它的 get 方法。注意,重載的時候,別忘了寫上 override 修飾符。重載完這個屬性之後,它會被自動調用,而且是在 PreInit 事件觸發時調用,我們不需要再寫一個 PreInit 事件的處理方法。
(b) 由於 PreInit 事件觸發得非常早,我們甚至連控件也看不到。在這裏唯一可以獲取的就是用戶的 http請求數據,而且還是原始數據。由於 Cookies 也是隨着 http 數據包一起發送的,所以之前寫入到Cookies 的主題名字還是在這裏能夠找到的。我們要做的就是取出之前存儲的主題名字,然後根據這個名字設置主題。注意,Cookies 數據是可以被篡改的,一定要做充足的檢查。
參考代碼:
public override string StyleSheetTheme
{
get
{
// 這個方法的調用時機是在 PreInit 事件的處理方法裏首先判斷用戶的 Cookies 裏有沒有 themeName 這一項
if (this.Request.Cookies["themeName"] != null)
{
// 如果 themeName 存在,那麼先取出來。注意,Cookies 裏面能夠找到themeName 這一項,不代表它的 Value 一定存在
string themeName = this.Request.Cookies["themeName"].Value;
// 進一步判斷取到的 themeName 是不是真的有數據
if (!string.IsNullOrEmpty(themeName))
{
// 如果字符串有內容,那還要再看看這個主題是不是真的存在,也就是檢查一下
// 對應的目錄是不是存在
// 這裏又要注意了(光 TM 注意了),"/App_Themes/" 是網站裏的虛擬目錄
// 需要先利用 Server.MapPath 把它轉換爲服務器操作系統的硬盤路徑,
// 這樣 System.IO 裏面的類才能找到對應的文件或者目錄。
string themePath = this.Server.MapPath("/App_Themes/" + themeName);
// 使用下面的方法,可以判斷目錄是否存在
if (System.IO.Directory.Exists(themePath))
{
// 到此爲止,這個主題纔算真能用
return themeName;
}
}
}
// 上面進行了一系列檢查,只要有一項不通過,就會走到這裏。我們返回默認主題
// 如果默認主題都不能用,啊.......還是去問問佛祖怎麼辦吧.......
return "Blue";
// 主題設置完畢了,此時嘗試更換主題,會發現有效果了,但是 DropDownList 的
// 狀態不對,導致換不回去。那麼,該去看 MasterPage.master.cs 了。
}
}
- 最後在具體的aspx頁面,繼承這個類。
Step 8:更新 DropDownList 的狀態
解決這個問題的關鍵是,判斷頁面是不是通過 PostBack 提交過來的。如果是首次打開, 那麼 DropDownList 的狀態只能根據 Cookies 設置,自然不會出錯。如果是用戶提交過 來的,DropDownList 可能被改變,此時 Cookies 裏的數據和提交過來的 DropDownList
狀態會不一致(Cookies 裏的數據是過時的)。此時要根據 DropDownList 來設置, 由於它自帶 ViewState,可以放手不管。
在MasterPage.master.cs 的Page_Load函數設置處理處理方法。
sideInfo.Text += selectTheme.SelectedItem.Text + "<br/>";
if (!IsPostBack)
{
# region UpdateSelection
// 首先判斷用戶的 Cookies 裏有沒有 themeName 這一項
if (this.Request.Cookies["themeName"] != null)
{
// 如果 themeName 存在,那麼先取出來。注意,Cookies 裏面能夠找到
// themeName 這一項,不代表它的 Value 一定存在
string themeName = this.Request.Cookies["themeName"].Value;
// 進一步判斷取到的 themeName 是不是真的有數據
if (!string.IsNullOrEmpty(themeName))
{
// 由於 Cookies 數據不可靠,所以要先檢查一下用戶選的主題,在
// 列表裏有沒有。使用 DropDownList.Items.FindByText (這個
// 方法真夠隱蔽的,第二次寫居然沒找到......)
ListItem anItem = this.selectTheme.Items.FindByText(themeName);
if (anItem != null)
{
// 如果這個主題真的有,那麼可以設置了。注意,先把原來
// 選擇的選項清除掉,然後再設置。否則報錯,說同時選了多個
// (這個設計應該是微軟偷懶了,按理說,更變選擇,可以自動
// 清除原來的纔對,不就是一個事件嘛)
this.selectTheme.SelectedItem.Selected = false;
anItem.Selected = true;
sideInfo.Text += "Selection changed<br />";
}
}
}
# endregion
}
sideInfo.Text += selectTheme.SelectedItem.Text + "<br/>";
注:到此爲止,動態切換主題就完成了。恭喜你走到了最後。最後的總結:如果感覺自己的思路沒有錯,但是結果卻非常不正常,此時請保持冷靜,去對照 ASP.NET 頁面生命週期,一步一步看看,是不是忽略了一些過程。
總結
以上大部分的註釋和說明採用老師的註釋,不是筆者寫,但是感覺老師寫的很不錯,因此發到網上,作爲參考,可以提供學習。步驟比較繁瑣,需要一步步的去做,若有疑問,可以留言,歡迎大家指出問題。
參考代碼的鏈接:
筆者編寫的代碼:https://pan.baidu.com/s/17hVPFrN09odusl4AEU3KTA
提取碼:8rlr