DotText的換膚實現起來挺複雜的,閱讀了代碼後把這塊的心得記錄下來.
1.先簡單說哈結構,(詳細的結構描述可以看"DotText源碼閱讀(6) --模版皮膚 ")
其實就是一些使用者控制項.這裡主要有個使用者控制項叫"PageTemplate.ascx"這個相當於是整套模版的Index.
每套模版放在一個檔案夾內(比如winxpBlue),"PageTemplate.ascx"和css這些在這個檔案夾的根目錄,然後使用者控制項組在這個檔案夾下的Controls裡面 ,注意,使用者控制項的後台代碼不是
跟ascx放在一起的,而是在網站根目錄的UI\Controls下,也就是說各套模版共用同樣的cs.
2.再來說哈主要相關的頁面和類.
主要類就是Dottext.Web.UI.WebControls.MasterPage,這個類是整個換膚的主類,是個容器,是最關鍵的.它用來裝載使用者控制項,具體操作下面再說
另外一個類就是ContentRegion,這個類是個Panel下繼承來的,主要用來裝在"內容"的使用者控制項
其實DotText在處理頁面配置時,已經劃分了好了,就是一個Head,一個Foot,一個LeftColumn(工具箱),還有一個"內容"(這個用來裝載內容的,比如裝載詳細資料,資訊列表之類的,而上面提到的ContentRegion就是用來裝載這個的)
現在看default.aspx的設定
<%@ Page language="c#" Codebehind="default.aspx.cs" AutoEventWireup="false" Inherits="Dottext.Web.UI.Pages.DottextMasterPage"%>
<%@ Register TagPrefix="DT" Namespace="Dottext.Web.UI.WebControls" Assembly="Dottext.Web" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title><asp:Literal ID="pageTitle" Runat="server" /></title>
<meta content=".Text" name="GENERATOR">
<link id="MainStyle" type="text/css" rel="stylesheet" runat="Server"/>
<link id="SecondaryCss" type="text/css" rel="stylesheet" runat="Server"/>
<link id="RSSLink" title="RSS" type="application/rss+xml" rel="alternate" runat="Server"/>
</HEAD>
<body>
<form id="Form1" method="post" runat="server">
<DT:MASTERPAGE id="MPContainer" runat="server">
<DT:contentregion id="MPMain" runat="server">
<asp:PlaceHolder id="CenterBodyControl" runat="server"></asp:PlaceHolder>
</DT:contentregion>
</DT:MASTERPAGE></form>
</body>
</HTML>
這裡default的後台Dottext.Web.UI.Pages.DottextMasterPage,一會我們再說這個幹了什麼.
現在注意頁面,一個MasterPage控制項,裡麵包含了一個id為MPMain的ContentRegion的Panel,在Panel上有一個PlaceHolder.
3,現在來說到底是怎麼工作的
在說明怎樣工作之前,要看哈DotText的UrlReWriting(詳細的可以看"dotText源碼閱讀(5)--URLreWrite和Handler ").DotText是用HttpHandle來實現UrlReWriting的 .
注意:其實使用者訪問的每一個頁面都是轉向到default.aspx,而之所以顯示不同的內容是因為模版上的ID為"MPMain"的ContentRegion裝載了不同的使用者控制項而已.
我們看哈WebConfig上關於Handle的Config上的設定
<HandlerConfiguration defaultPageLocation="default.aspx" type="Dottext.Common.UrlManager.HandlerConfiguration, Dottext.Common">
<HttpHandlers>
<HttpHandler pattern="/articles/\d+\.aspx$" controls="viewpost.ascx,Comments.ascx,AnonymousPostComment.ascx,LoginPostComment.ascx" />
<HttpHandler pattern="/articles/\w+\.aspx$" controls="viewpost.ascx,Comments.ascx,AnonymousPostComment.ascx,LoginPostComment.ascx" />
<HttpHandler pattern="/PreviewPost.aspx$" controls="PreviewPost.ascx" />
<HttpHandler pattern="/archive/\d{4}/\d{2}/\d{2}/\d+\.(aspx|htm)$" controls="viewpost.ascx,Comments.ascx,AnonymousPostComment.ascx,LoginPostComment.ascx" />
<HttpHandler pattern="/archive/\d{4}/\d{2}/\d{2}/\w+\.(aspx|htm)$" controls="viewpost.ascx,Comments.ascx,AnonymousPostComment.ascx,LoginPostComment.ascx" />
<HttpHandler pattern="/archive/\d{4}/\d{1,2}/\d{1,2}\.aspx$" controls="ArchiveDay.ascx" />
<HttpHandler pattern="/archive/\d{4}/\d{1,2}\.aspx$" controls="ArchiveMonth.ascx" />
<HttpHandler pattern="/archives/\d{4}/\d{1,2}\.aspx$" controls="ArticleArchiveMonth.ascx" />
<HttpHandler pattern="/contact\.aspx$" controls="Contact.ascx" />
<HttpHandler pattern="/AddToFavorite\.aspx$" handlerType="Page" pageLocation="AddToFavorite.aspx" />
<HttpHandler pattern="/BlogSearch\.aspx$" controls="BlogSearch.ascx" />
<HttpHandler pattern="/posts/|/story/|/archive/" type="Dottext.Web.UI.Handlers.RedirectHandler,Dottext.Web" handlerType="Direct" />
<HttpHandler pattern="/gallery\/\d+\.aspx$" controls="GalleryThumbNailViewer.ascx" />
<HttpHandler pattern="/gallery\/image\/\d+\.aspx$" controls="ViewPicture.ascx" />
<HttpHandler pattern="/(?:category|stories)/(\w|\s)+\.aspx$" controls="CategoryEntryList.ascx" />
<HttpHandler pattern="/favorite/(\w|\s)+\.aspx$" controls="FavoriteList.ascx" />
<HttpHandler pattern="/(?:admin)" type="Dottext.Web.UI.Handlers.BlogExistingPageHandler, Dottext.Web" handlerType="Factory" />
</HttpHandlers>
</HandlerConfiguration>
這裡我只想說哈 <HttpHandler pattern="/PreviewPost.aspx$" controls="PreviewPost.ascx" /> 類型的配置, 比如這裡,我們訪問PreviewPost.aspx,那麼會
在後台執行 Dottext.Common.UrlManager.HandlerConfiguration的GetHandle
public virtual IHttpHandler GetHandler(HttpContext context, string requestType, string url, string path)
{
.....................
.....................
switch(items[i].HandlerType)
{
case HandlerType.Page://預設是Page
return ProccessHandlerTypePage(items[i],context,requestType,url);
case HandlerType.Direct:
HandlerConfiguration.SetControls(context,items[i].BlogControls);
return (IHttpHandler)items[i].Instance();
case HandlerType.Factory:
//Pass a long the request to a custom IHttpHandlerFactory
return ((IHttpHandlerFactory)items[i].Instance()).GetHandler(context,requestType,url,path);
default:
throw new Exception("Invalid HandlerType: Unknown");
}
..............................
....................
}
而由於這種 PreviewPost.aspx的HandlerType是Page,所以會執行 ProccessHandlerTypePage .而這個主要執行的工作就是SetControls,即把配置中的這個 "PreviewPost.ascx"儲存到HttpContext裡面,方便初始化頁面的時候調用(調用用GetControls方法);
最後地址仍然是被轉向到了default.aspx.
現在來看初始化default.aspx頁面,首先裝載使用者控制項,default.aspx的後台代碼是Dottext.Web.UI.Pages.DottextMasterPage, 在OnInit裡面執行InitializeBlogPage函數,在這裡讀取HttpContext裡面儲存的使用者控制項名,裝載它到MPMain上,而對於default.aspx上的使用者控制項MasterPage所做的操作是這樣的,首先由於在請求時會執行事件AddParsedSubObject,在這裡把default.aspx上定義的所有ContentRegion變數存入變數contents裡面(即"MPMain").
然後在MasterPage的OnInit裡面做了兩件事:
一個是BuildMasterPage,在這裡從BlogConfig裡讀模數版路徑(例如:"WinXPBlue\PageTempalte.ascx"),然後載入它.
另一個是BuildContents,用來初始化架構上內容這塊的. 根據contents裡面儲存的各個ContentRegion變數名和頁面上已經裝載的控制項想比較,找到contents裡面儲存的那個ContentRegion變數(即"MPMain"),然後載入它.(注意:因為PageTemplate.ascx也定義了一個ID為MPMain的ContentRegion類的變數,這部做的其實就是載入PageTemplate裡面定義的MPMain下的使用者控制項了)
好了,現在重新來看哈整個執行過程
1.訪問PreviewPost.ascx
2.將字串"PreviewPost.ascx"加入到HttpContext
3.轉向到default.aspx
4.default.aspx載入Context裡面儲存的 PreviewPost.ascx 到頁面上的MPMain上.
5.default.aspx裝載MaterPage
6.MasterPage載入PageTemplate.ascx,並把它裡面的子控制項都載入在自己的Controls上.由於MasterPage重寫了AddParsedSubObject,所以Default.aspx上的MPMain不會出現在MasterPage的Controls裡面
7.MasterPage處理MPMain,將變數contents裡面儲存的default.aspx上的MPMain的控制項剪下到從PageTemplate.ascx上載入的MPMain上去.
8.到此default.aspx輸出的就是這樣的:Head,Foot,Left工具箱是從PageTemplate.ascx上載入來的,而"內容"這塊是PreviewPost.ascx了.