《Asp.Net Forums2.0深入分析》之 Asp.Net Forums是如何?代碼分離和換

來源:互聯網
上載者:User
asp.net|代碼分離 在Visual Studio中開發Web項目,Web Form頁由兩部分組成:視覺元素(HTML、伺服器控制項和靜態文本)和該頁的編程邏輯。 一般將這兩個組成部分分別儲存在一個單獨的檔案中。可視元素在一個 .aspx 檔案中建立,而代碼位於一個單獨的類檔案中(.aspx.vb 或 .aspx.cs)。或者有時候也會在同一檔案中建立視覺元素和代碼。

而在Asp.Net Forums的Web表單頁中沒有找到我們熟悉的.aspx.cs檔案,也沒有發現任何C#代碼,取而代之是一個個控制項,代碼在哪裡?!

下面將以login.aspx為例詳細說明Asp.Net Forums是如何?代碼分離和換皮膚的:
首先我們看看login.aspx在兩種皮膚樣式下的運行效果
(Theme:default)(Theme:ElectricMidnight)

只是更改了一下Asp.Net Forums的預設皮膚,同樣是Login.aspx,顯示的是兩種不同的皮膚樣式。先回想一下VS.Net中,先不論換皮膚功能,如果我們要實現一個登陸頁面,那麼我們在Aspx或Ascx頁中將輸入帳號密碼的TextBox、登陸的Button拖入,在編輯區雙擊Button,寫上對Button點擊事件處理的代碼,多麼方便,大部分代碼都由VS.Net為我們完成了。

我們再來看Login.aspx的源碼:


<%@ Import Namespace="AspNetForums.Components" %>
<%@ Register TagPrefix="Forums" Namespace="AspNetForums.Controls" Assembly="AspNetForums.Controls" %>
<%@ Register TagPrefix="mp" Namespace="MetaBuilders.WebControls.MasterPages" Assembly="MetaBuilders.WebControls.MasterPages" %>

<mp:ContentContainer runat="server" id="MPContainer" MasterPageFile="~/Themes/MasterPage.ascx">
<mp:Content id="MainContent" runat="server">
<p align="center">
<Forums:NavigationMenu DisplayTitle="true" id="Navigationmenu1" runat="server" />
<br />
<br />
<br />
<Forums:Login runat="server" id="PostView1" />
</p>
</mp:Content>
</mp:ContentContainer>
註:其中 <mp:***> ,這個是一個第三方控制項,其目的是為了保證介面的一致性,提取頁面間的重複代碼。

從源碼中我們沒有看到任何構成Login.aspx頁面效果的TextBox、Button等基本元素。甚至沒有發現一行C#代碼,不過如果您對頁面控制項比較熟悉不難發現原來Asp.Net Forums中將登陸的介面封裝為了控制項(在此對頁面控制項並不作專門介紹,如果您對控制項相關知識還比較陌生的話,強烈推薦您查閱相關資料或書籍)。 原來登陸介面的實現就是在<Forums:Login runat="server" id="PostView1" />控制項中,從
<%@ Register TagPrefix="Forums" Namespace="AspNetForums.Controls" Assembly="AspNetForums.Controls" %>
我們可以知道Login控制項對應的類應該為:AspNetForums.Controls.Login,在VS.Net中,切換到類別檢視,找到AspNetForums.Controls.Login並轉到對應檔案:

(該圖告訴您如何快速的尋找控制項對應的檔案)
 
從代碼中看到的該控制項是從SkinnedForumWebControl類繼承的:

public class Login : SkinnedForumWebControl {  // 從 SkinnedForumWebControl 基類繼承
......
}

我們還是先看看基類SkinnedForumWebControl。
using System;
using System.Drawing;
using System.Collections;
using System.Collections.Specialized;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using AspNetForums;
using AspNetForums.Components;
using System.ComponentModel;
using System.IO;
using System.Web.Security;
using AspNetForums.Enumerations;

namespace AspNetForums.Controls {

[
ParseChildren(true)
]
/// <summary>
/// 幾乎Asp.Net Forums中所有控制項的基類,繼承自WebControl,並實現INamingContainer介面
/// </summary>
public abstract class SkinnedForumWebControl : WebControl, INamingContainer {

ForumContext forumContext = ForumContext.Current;
string skinFilename = null;
string skinName = null;
string returnURL = null;
ForumMode mode = ForumMode.User;


public SkinnedForumWebControl() {

// 使用的皮膚——如果是匿名使用者,則使用系統預設樣式
//
if (forumContext.User.IsAnonymous) {
skinName = Globals.Skin;
}
else {
skinName = forumContext.User.Theme;
}

}



/// <summary>
/// 當開發複合伺服器控制項或模板伺服器控制項時,必須重寫此方法。
/// 通知使用基於合成的實現的伺服器控制項建立它們包含的任何子控制項,以便為回傳或呈現做準備。
/// </summary>
protected override void CreateChildControls() {
Control skin;

// 裝載使用者控制項
skin = LoadSkin();

// 初始化控制項
InitializeSkin(skin);

Controls.Add(skin);
}


/// <summary>
/// 通過SkinName和SkinFilename找出使用者控制項檔案的路徑,裝載該使用者控制項後的Control對象
/// </summary>
/// <returns></returns>
protected Control LoadSkin() {
Control skin;
// 使用者控制項檔案所在位置
string skinPath = Globals.GetSkinPath() + "/Skins/" + SkinFilename.TrimStart('/');
string defaultSkinPath = Globals.ApplicationPath + "/Themes/default/Skins/" + SkinFilename.TrimStart('/');

// 必須要有SkinFilename屬性
if (SkinFilename == null)
throw new Exception("You must specify a skin.");

// 從使用者控制項檔案擷取 UserControl 對象。
try {
skin = Page.LoadControl(skinPath);
}
catch (FileNotFoundException) {

// 如果沒有找到指定皮膚的使用者控制項檔案,裝載預設皮膚下的控制項檔案
try {
skin = Page.LoadControl(defaultSkinPath);
}
catch (FileNotFoundException) {
throw new Exception("Critical error: The skinfile " + skinPath + " could not be found. The skin must exist for this control to render.");
}
}
return skin;
}

/// <summary>
/// 初始化控制項,並繫結控制項資料
/// </summary>
/// <param name="skin"></param>
protected abstract void InitializeSkin(Control skin);

/// <summary>
/// 使用者控制項檔案(*.ascx)路徑
/// </summary>
public string SkinFilename {
get {
return skinFilename;
}
set {
skinFilename = value;
}
}


/// <summary>
/// 皮膚名
/// </summary>
protected string SkinName {
get {
return skinName;
}
set {
skinName = value;
}
}

public ForumMode Mode {
get { return mode; }
set { mode = value; }
}

}
}
 
從代碼中可以看出,基類SkinnedForumWebControl繼承自WebControl類並實現了INamingContainer介面。既然是自訂控制項,自然就是從WebControl類繼承。之所以實現INamingContainer介面,是因為在開發樣板化控制項時,應實現該介面以避免同一頁上的命名衝突。
在SkinnedForumWebControl中有兩個重要的屬性:SkinName 和 SkinFilename,分別表示皮膚名和使用者控制項檔案路徑。在Asp.Net Forums2.0中,在Web目錄下有一個Themes目錄,每種皮膚對應一個目錄,例如default、ElectricMidnight,每個皮膚檔案夾下有三個檔案夾:image、Skins和style,分別用來存放該皮膚下對應的圖片檔案、使用者控制項檔案(*.ascx)和樣式表Css檔案。通過這兩個屬性,我們可以知道使用者控制項檔案(*.ascx)的真實路徑,例如我們的SkinName是default,SkinFilename是skin-login.ascx,那麼使用者控制項的路徑是Themes/default/skins/skin-login.ascx,同理,如果我們將皮膚樣式換成ElectricMidnight,那麼使用者控制項的路徑將是 Themes/ElectricMidnight/skins/skin-login.ascx。

還有兩個重要的方法,一個是LoadSkin(),在該方法中,首先根據上面介紹的兩個屬性找出使用者控制項檔案的路徑,然後通過Page.LoadControl(defaultSkinPath)方法,從使用者控制項檔案中擷取 UserControl 對象。這也就是為什麼皮膚不同,頁面樣式就不同,因為隨著皮膚的不同,我們Load的使用者控制項檔案也不同,我們只要將使用者控制項檔案樣式設定的不一樣,就可以隨著皮膚的不同顯示的樣式也不一樣。
 


但是光這些還不夠,我們還需要識別ascx頁中的頁面控制項。例如在skin-login.ascx中,我們必須知道哪個輸入框是帳號的,那個輸入框是密碼的,知道使用者是否點擊了登陸按鈕。回想VS.Net中,IDE自動幫我們把這些控制項根據ID識別出來,在IDE中雙擊按鈕,就可以直接加上響應點擊事件的代碼,多麼方便。但是現在我們該如何?……

基類中還有一個抽象的InitializeSkin(Control skin)方法,所有繼承自SkinnedForumWebControl的控制項都必須override該方法,因為我們可以在這個方法中來初始化控制項,在LoadSkin()方法中我們已經通過Page.LoadControl(defaultSkinPath)方法返回了一個UserControl,現在我們在InitializeSkin(Control skin)中通過Control.FindControl 方法,在UserControl中搜尋指定的伺服器控制項,並對控制項進行綁定資料和事件。還是以Login控制項為例,摘取AspNetForums.Controls.Login類中的部分代碼如下:

public class Login : SkinnedForumWebControl {  // 從 SkinnedForumWebControl 基類繼承
string skinFilename = "Skin-Login.ascx"; // 預設皮膚檔案
TextBox username; // 帳號輸入框
TextBox password; // 密碼輸入框
Button loginButton; // 登陸按鈕
}
public Login() : base() {
if (SkinFilename == null)
SkinFilename = skinFilename; // 定義預設的皮膚檔案
}
// 重寫 InitializeSkin 初始化
override protected void InitializeSkin(Control skin) {

// 尋找ascx頁中ID是username的textbox控制項
username = (TextBox) skin.FindControl("username");

// 尋找ascx頁中ID是password的textbox控制項
password = (TextBox) skin.FindControl("password");

// 找到登陸按鈕
loginButton = (Button) skin.FindControl("loginButton");
loginButton.Click += new System.EventHandler(LoginButton_Click); // 綁定登陸按鈕的Click事件
loginButton.Text = ResourceManager.GetString("LoginSmall_Button");

}

在skin-login.ascx中,我們的每個控制項都有一個ID,例如使用者名稱輸入框的ID是username,這樣,我們就可以在重寫InitializeSkin(Control skin)的時候,利用username = (TextBox) skin.FindControl("username");這樣的方法來一個個找到對應使用者控制項檔案中的控制項。並可以對綁定資料和事件,例如:loginButton.Click += new System.EventHandler(LoginButton_Click),綁定登陸按鈕的Click事件。
綜上所述,Asp.Net Forums就是通過自訂控制項來實現代碼分離的,並通過在控制項中動態裝載使用者控制項檔案(*.aspx)來實現換皮膚功能的。當您在看Asp.Net Forums2.0原始碼的時候,再也不要被<Forums:NavigationMenu DisplayTitle="true" id="Navigationmenu1" runat="server" />這樣的代碼所嚇倒,直接切換到類別檢視,找到對應的類:AspNetForums.Controls.NavigationMenu,從類代碼中……

如果您還不是很理解,您可以自己研讀一下Asp.Net Forums2.0的源碼,如果您覺得太複雜,請看類比Asp.Net Forums實現可以換皮膚的控制項一文的例子,也許有助您理解:)




相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.