ASP.NET2.0的導航系統確實給web開發帶來方便,但是用過的使用者就會發現導航系統有一個很大的缺陷:他需要你手工編寫web.sitemap,web.sitemap的文法是“相當的簡單”,但是實際運用時,雖然簡單,對於稍微複雜的導航,你肯定容易出錯。下面是一個簡單的sitemap,
<siteMap>
<siteMapNode title="Home" url="~/default.aspx" >
<siteMapNode title="Introduction to ASP.NET" url="~/introduction/default.aspx">
<siteMapNode title="What's New in Whidbey?" url="~/introduction/whatsnew.aspx"/>
<siteMapNode title="Sample Applications (Starter Kits)" url="~/introduction/starterkits.aspx"/>
<siteMapNode title="Introduction to Visual Web Developer" url="~/introduction/vwd.aspx"/>
</siteMapNode>
<siteMapNode title="Building A Web Application" url="~/development/default.aspx">
<siteMapNode title="Building a Simple Application" url="~/development/simple/default.aspx">
<siteMapNode title="Introduction to ASP.NET pages" url="~/development/simple/pages.aspx"/>
<siteMapNode title="Introduction to Server Controls" url="~/development/simple/servercontrols.aspx"/>
<siteMapNode title="Inline vs Code Behind Pages" url="~/development/simple/codeseparation.aspx"/>
<siteMapNode title="Sharing Code Between Pages" url="~/development/simple/codedirectory.aspx"/>
</siteMapNode>
</siteMap>
說白了,他只是一些siteMapNode 的嵌套,但是嵌套的開閉呼應對人而言,是一個煩點,但是對電腦來說,就喜歡處理這些簡單的關係,所以我們可以編寫一個檔案,讓系統自動檢索當前應用程式的頁面並自動產生導航。
首先定義一些變數
//私人成員
private SiteMapNode _root;
private String _rootTitle = "Home";
private string _rootUrl = "~/Default.aspx";
private bool _useDefaultPageAsFolderUrl = true;
private string _defaultPageName = "default.aspx";
private CacheDependency _fsMonitor;
private StringDictionary _excludeFileList;
private StringDictionary _excludeFolderList;
private char[] _listSeparator ={ ',' };
在這些變數中,_root變數存放網站的根目錄,另外定義了_rootTitle和_rootUrl,使用這兩個變數存放根目錄的串連標題和連結地址。
你可能發現我還定義了CacheDependency 類型的_fsMonitor,在本導航裡,使用了緩衝技術而且強烈推薦你使用緩衝,因為網站導覽將來在各個頁面使用,如果每次都由系統自動根據檔案清單產生導航,那麼將明顯影響效能,所以這裡使用了緩衝,基本思想是:如果使用者沒有更改網站下的頁面,就直接從緩衝裡擷取導航資訊,否則,就重建網站導覽,產生新導航後,同樣會複製一份副本到到當前緩衝裡。
在上面的變數裡還有兩個變數:_excludeFileLis和_excludeFolderList,顧名思義,這兩個變數表示將來我想要排斥在導航裡的檔案和檔案夾。什麼意思呢?
由於我們是讓系統枚舉根目錄下的所有檔案,所以系統將預設根據該目錄下的所有檔案產生所有網站導覽,這顯然並不能夠完全滿足我們的需求。
在後面實現的代碼裡,你可以發現如下一段代碼:
String folder = HttpContext.Current.Server.MapPath(folderPath);
DirectoryInfo dirInfo = new DirectoryInfo(folder);
foreach (FileInfo fi in dirInfo.GetFiles("*.aspx"))
{
SiteMapNode fileNode = CreateFileNode(fi.FullName, parentNode, folderPath);
if (fileNode != null)
AddNode(fileNode, parentNode);
}
foreach (DirectoryInfo di in dirInfo.GetDirectories())
{
SiteMapNode folderNode = CreateFolderNode(di.FullName, String.Concat(folderPath,
di.Name, "/") + DefaultPageName);
if (folderNode != null)
AddNode(folderNode, parentNode);
BuildSiteMapFromFileSystem(folderNode, String.Concat(folderPath, di.Name, "/"));
}
這段代碼顯示了產生的導航其實是枚舉每一個檔案夾下的aspx頁面,但是可能有時候我們並不希望他枚舉所有目錄下的aspx頁面,(包括App_Code, images,等,如果有的話)以及其他為了安全而不想讓系統顯示的aspx頁面,
_excludeFileLis和_excludeFolderList就可能讓我們告訴系統哪些檔案應該排除在導航裡。
_excludeFileLis和_excludeFolderList的值來自於web.config,你可以在web.config裡增加如下配置:
<add name="FileSystemSiteMapProvider"
type="FileSystemSiteMapProvider"
ExcludeFileList="default1.aspx,default2.aspx"
ExcludeFolderList="myfiles,myfile2"
/>
通過對web.config裡對外公開兩個排斥檔案的介面,就可以讓客戶輕而易舉配置導航實際產生的需求。
在代碼裡可以看到如下一句代碼:
public void Refresh()
{
_root = null;
base.Clear();
}
前面曾經說過,為了提高效能,使用了緩衝技術,當緩衝失效以後,就會調用Refresh重新整理系統,這個失效是是通過調用base.Clear();來實現的
在構建網站導覽時,是利用BuildSiteMap函數完成,這是實現導航的核心函數,該函數的代碼如下
public override System.Web.SiteMapNode BuildSiteMap()
{
lock (this)
{
if (_root != null)
if (!_fsMonitor.HasChanged)
return _root;
Refresh();
_root = CreateFolderNode(HttpContext.Current.Server.MapPath(RootUrl), RootUrl);
_root.Title = RootTitle;
_fsMonitor = new CacheDependency(HttpContext.Current.Server.MapPath("~/"));
AddNode(_root);
BuildSiteMapFromFileSystem(_root, "~/");
return _root;
}
你可以發現在這裡使用了
lock()
{
}
進行鎖定,因為我們希望系統在更新時實行類似“資料庫裡事務”的操作,一次更新要麼全做,要麼全不做,最根本的原因是因為使用者請求的並發性。
在這個導航裡使用了自訂的Provider模型,為了簡化開發,我們使用了的FileSystemSiteMapProvider 類,該類從StaticSiteMapProvider類派生,這也是微軟推薦的一種模式。現在在web.config裡添加如下配置
<siteMap enabled="true" defaultProvider="FileSystemSiteMapProvider">
<providers>
<add name="FileSystemSiteMapProvider"
type="FileSystemSiteMapProvider"
/>
</providers>
</siteMap>
就可以了
下面是某一個頁面(*.aspx)使用它的基本模式
<asp:TreeView ID="TreeView1" runat="server" DataSourceID="SiteMapDataSource1"
ImageSet="XPFileExplorer"
NodeIndent="15" ShowLines="True" MaxDataBindDepth="5">
<ParentNodeStyle Font-Bold="False" />
<HoverNodeStyle Font-Underline="True" ForeColor="#6666AA" />
<SelectedNodeStyle BackColor="#B5B5B5" Font-Underline="False"
HorizontalPadding="0px"
VerticalPadding="0px" />
<NodeStyle Font-Names="Tahoma" Font-Size="8pt" ForeColor="Black"
HorizontalPadding="2px"
NodeSpacing="0px" VerticalPadding="2px" />
</asp:TreeView>
<asp:SiteMapDataSource ID="SiteMapDataSource1" runat="server" />
正如你所看到的,使用步驟如下
1)在aspx頁面放置一個asp:SiteMapDataSource,無須任何設定
2)然後使用treeview(menu,...)設定其datasourceid為SiteMapDataSource1即可。
就這麼簡單,順便說一下,如果你使用後,突然又想使用預設的web.sitemap檔案,作為最簡單的處理方式,只要更改web.config裡的defaultProvider為XmlSitemap即可
本文願代碼下載與說明
此處是完成原始碼,如果你需要在你的項目裡使用,只要把該檔案copy到應用程式的App_Code目錄下即可(副檔名請更改為cs檔案)。