原代碼下載:
WebParts.exe (619KB)
原文出處:
ASP.NET 2.0 Personalize Your Portal with User Controls and Custom Web Parts
本文基于 ASP.NET 2.0 的預發(fā)行版本,文中提供的所有信息將來都可能發(fā)生變化。
本文將討論以下內(nèi)容:
- 使用Web部件創(chuàng)建模塊化的Web門戶應用;
- 個人化特性和自定義特性;
- 將自定義用戶控件作為Web部件使用;
- 創(chuàng)建一個個人化特性的提供程序;
現(xiàn)今門戶應用非常流行,好的門戶都有共同而顯著的特點。那就是都會給訪問者提供雅觀的信息,并且這些信息都是通過模塊化的、一致的、易于瀏覽的用戶界面提供的。一些綜合性的門戶網(wǎng)站走得更遠,它們甚至允許網(wǎng)站成員提供內(nèi)容、上傳文檔以及個人化門戶頁面。
微軟為 Windows Server 2003 平臺增加了一個可擴展的門戶應用框架,隨之發(fā)布了一個 Windows SharePoint 服務 ,這個框架提供了門戶應用框架必須的一些基本元素,其中包括站點成員的支持、內(nèi)容和文檔管理、使用 Web 部件以模塊化的形式展示數(shù)據(jù)等等。
Web 部件提供了支持自定義特性和個人化特性的基礎功能。在Windows SharePoint服務網(wǎng)站里面,通過配置站點,門戶應用的用戶能夠添加、配置、刪除Web部件,這樣他們就能輕松地個人化或者定制頁面了;赪indows SharePoint服務的站點還提供了一種簡便而且強大的方法擴展站點的功能,那就是:開發(fā)自定義Web部件。創(chuàng)建支持定 制特性和個人化特性的Web部件時,你只需要簡單地在你的Web部件類里面增加一些屬性以及設置幾個特殊的標簽就可以了。那些繁瑣復雜的工作都由 Windows Sharepoint 服務的Web部件基礎結構來完成,比如:序列化、存儲和讀取與站點自定義特性和成員個人化特性相關的數(shù)據(jù)。
ASP.NET 2.0 引入了一套Web部件控件集,這套控件集與 Windows SharePoint 服務提供的功能很相似,它們 被設計用來完成序列化、存儲和讀取站點自定義特性和成員個人化特性相關數(shù)據(jù)等功能。但是它們更獨特和更靈活,它們與 SQL Server 或 Active Directory 不是緊耦合的。對于那些希望使用基于表單驗證技術建立門戶,或者不想受限于某一特定數(shù)據(jù)庫解決方案的公司來講,這無疑是一個好消息。
圖1:使用模塊化Web部件設計的一個示例門戶應用
本文將向你展示一個用 ASP.NET 2.0 Web 部件開發(fā)的示例門戶應用,其主要目的是讓你了解為門戶應用開發(fā)Web部件時 ,你將面臨的一些重要的設計問題。首先,我們將著重介紹新的 ASP.NET 2.0 Web部件控件集涉及的一些基本概念和控件類型。例子參見圖1。
Web部件基礎
用來放置Web部件的頁面,我們可以稱之為Web部件頁面。如圖2所示,一個Web部件頁面需要一個WebPartManager 控件(只能有一個)和一個或多個的 WebPartZone 控件。還可以包括一個 EditorZone 控件或者 CatalogZone 控件(不是必需的)。需要注意的是,在.aspx文件中,WebPartManager 控件的標簽必須出現(xiàn)在與Web部件 基礎結構相關的任何其它控件標簽之前,比如:WebPartZone 控件、EditorZone 控件、CatalogZone 控件。為了更好地控制Web部件頁面的布局和表現(xiàn)形式,你還可以在aspx文件中使用HTML表格,將不同的 zone 控件布局到不同的地方。
圖2:一個Web部件頁面的典型布局
我們先來看一個簡單的Web部件頁面例子,該頁面包含 WebPartManager 控件和 WebPartZone 控件:
<asp:WebPartManager ID=" WebPartManager1" runat="server" />
<asp:WebPartZone ID="WebPartZone1" runat="server" HeaderText="Zone 1">
<ZoneTemplate>
<!-- time to add a Web Part -->
</ZoneTemplate>
</asp:WebPartZone>
在頁面里放置了一個 WebPartZone 控件之后,就可以使用Web部件定義來創(chuàng)建Web部件實例了。有二種不同的方法可以創(chuàng)建Web部件定義。第一種方法是創(chuàng)建一個從 WebPart 類繼承的類,第二種方法是創(chuàng)建一個用戶控件。 本文稍后的部分,我們將詳細探討這兩種方法之間的不同,F(xiàn)在,我們先創(chuàng)建一個從WebPart類繼承的簡單的類(參見
圖3)。
每個Web部件實例都存在于一個頁面特定的 WebPartZone 控件的某個索引位置上。一個 WebPartZone 控件可以包含多個Web部件。如圖4所示,在 WebPartZone1 控件里面有二個Web部件實例。
圖4:在一個特定zone控件中的Web部件
你可以通過編程方式和聲明方式將Web部件添加到某個區(qū)(zone)中。稍后你還可以看到如何將Web部件添加到部件目錄中。當你創(chuàng)建了一個 CatalogPart 控件以后,用戶就可以在運行時將一個新的Web部件添加到WebPartZone控件中。
以編程方式將Web部件添加到 WebPartZone 控件里面的方法取決于需要添加的Web部件的類型。如果是一個從 WebPart 繼承的類,需要以編程方式創(chuàng)建一個 該類的實例,然后調(diào)用 WebPartManager 類的 AddWebPart 方法。調(diào)用 AddWebPart 方法時,你需要傳入的參數(shù)包括:Web部件的實例、 目標 WebPartZone 控件、以及Web部件在 WebPartZone 控件中的索引位置。代碼如下:
// create Web Part instance from WebPart-derived classWebPart wp1 = new WingtipWebParts.HelloWorld();WebPartManager1.AddWebPart(wp1, WebPartZone1, 0); 此即通過編程方式將Web部件添加到 WebPartZone。聲明方式是在Web部件頁面的.aspx文件中定義控件標簽。當用戶訪問頁面時,如果你希望某個Web部件出現(xiàn)在特定的 WebPartZone 控件中,你可以在 WebPartZone控件中添加一個 ZoneTemplate。代碼如下:
<%@ Register Assembly="WingtipWebParts" Namespace="WingtipWebParts"TagPrefix="Wingtip" %>
<asp:WebPartManager ID=" WebPartManager1" runat="server" />
<asp:WebPartZone ID="WebPartZone1" runat="server" HeaderText="Zone 1">
<ZoneTemplate>
<Wingtip:HelloWorld runat="server" id="HelloWorld" />
</ZoneTemplate>
</asp:WebPartZone>
別忘了給Web部件“打扮”一下,因為如果不這樣的話,你的Web部件看起來會很乏味的。內(nèi)聯(lián)門戶網(wǎng)應用需要的一個通用特性就是能夠給站點“換膚”,根據(jù)訪問者的喜好改變Web部件的 外觀。過去,這意味著你要自己創(chuàng)建一套架構去支持改變控件呈現(xiàn)方式的功能,而這需要做大量的工作。ASP.NET 2.0 引入了“主題”的概念,其中包括一系列的風格屬性和控件屬性,這些屬性可以應用到單個控件、整個頁面甚至整個應用的全局范圍。
為了讓你的門戶應用看起來顯得優(yōu)雅和專業(yè),你需要定制所有 WebPartZone 控件、EditorZone 控件和 CatalogZone 控件的 外觀。當你剛開始做這件事的時候,你會覺得這種工作非常乏味。因為你需要給Web部件、Editor部件以及Catelog部件的內(nèi)容區(qū)、標題欄和動態(tài)菜單等等區(qū)域換膚和修改顯示屬性。幸運的是,新的ASP.NET 2.0 的主題特性可以將這些.aspx文件中有關界面工作的成果提取到可以重用的.skin文件和.css文件中。這篇文章的示例門戶應用程序就是使用換膚和主題功能來定制Web部件的 外觀的,你可以到MSDN雜志的網(wǎng)站上下載。
顯示模式和頁面范圍
WebPartManager 控件的一個單一實例運行在每個Web部件頁面中,它負責管理Web部件實例以及Web部件如何與WebPartZone控件交互。WebPartManager控件還提供了一個可編程接口,可以用來切換Web部件頁面的顯示模式,例如:你可以在以下 三種模式之間切換:瀏覽模式、設計模式和編輯模式。例如:要通過程序將當前頁面切換到設計模式,你只需要添加一個鏈接控件,在這個控件的事件處理程序中將DisplayMode屬性設置為DesignDisplayMode即可。代碼如下:
WebPartManager1.DisplayMode = WebPartManager.DesignDisplayMode; 理解Web部件頁面每種顯示模式之間的區(qū)別是很重要的。默認情況下,Web部件頁面處于瀏覽模式,此模式不允許用戶修改任何的Web部件。當切換到設計模式的時候,用戶就能夠在WebPartZone內(nèi)部或者不同的 區(qū)之間移動Web部件了。
圖5列出了所有的顯示模式。
ASP.NET 2.0 Web部件控件會在后臺生成所有必需的DHTML和Javascript代碼,讓用戶在瀏覽器里可以進行拖放操作。在不支持必 需的DHTML和Javascript特性的瀏覽器里,除了拖放操作之外的所有其它編輯功能都可以使用。這意味著你不必擔心要強制所有的客戶使用Microsoft IE5.0或以上的瀏覽器。ASP.NET 2.0 Web部件控件還支持管理個人化數(shù)據(jù)的存儲和獲取,能夠記住用戶上一次將Web部件放在了什么地方。
除了支持使用代碼切換顯示模式之外,WebPartManager控件還提供了允許Web部件頁面在用戶范圍和共享范圍之間切換的方法。頁面范圍是指對Web部件的修改是自定義操作還是個人化操作。所有的用戶都能看到自定義操作的結果,而只有用戶自己才能看到個人化操作的結果。改變Web部件頁面的范圍可以通過調(diào)用WebPartManager 控件的 ToggleScope 方法來實現(xiàn),代碼如下:
WebPartManager1.Personalization.ToggleScope(); 默認范圍即用戶范圍,也就是說對Web部件的修改被記錄為個人化操作,只能被當前用戶看到。成功地調(diào)用ToggleScope方法會把Web部件頁面改變到共享范圍,這樣的話,對Web部件的修改就會被記錄為自定義操作。共享范圍的作用是為了允許管理員或者站點設計人員統(tǒng)一修改Web部件頁面中的Web控件,讓所有用戶都能看到這種改變。如果有沖突,個人化修改總是優(yōu)先于全局自定義操作。
當前用戶并不總是能夠成功地進入共享范圍。默認情況下,任何用戶都沒有這樣的權限。只有在Web.config文件中用戶被賦予了這樣的權限才行。下面是一個例子,它允許所有擁有admin或者site_designer角色的用戶可以進入共享范圍和修改全局自定義數(shù)據(jù):
<webParts>
<personalization>
<authorization>
<allow roles="admin, site_designer" verbs="enterSharedScope" />
</authorization>
</personalization>
</webParts>
屬性和個人化
每一個Web部件對象都有一套標準的屬性能夠被自定義或者個人化。例如,每個Web部件都有一個Title屬性,在被加入WebPartZone之后,還能夠被自定義。EditorZone控件和一些Editor部件允許用戶對Web部件屬性做修改。
當用戶將Web部件頁面切換為編輯模式時,Web部件的菜單提供了一個編輯命令。在一個Web部件上調(diào)用編輯命令會顯示出一個EditorZone控件,此控件是放在相關的 區(qū)控件中的。ASP.NET 2.0提供了一些內(nèi)置的Editor部件,用來修改標準的Web部件外觀、行為和布局。
通過添加一些能夠被個人化的自定義屬性,Web部件個人化配置數(shù)據(jù)能夠被輕松地擴展。你只需要在Web部件類的定義里面添加這些屬性,并且打上諸如:Personalizable、WebBrowsable、WebDisplayName之類的特性標簽就可以了。當你完成這些之后,Web部件控件會幫助你存儲和獲取這些被個人化或者自定義的屬性值。
當你創(chuàng)建某個個人化屬性時,通常會同時定義一個私有字段,代碼如下:
private bool _HR = true;
[Personalizable(PersonalizationScope.User), WebBrowsable, WebDisplayName("Show HR News"), WebDescription("Use this property to show/hide HR news")]
public bool HR
{ get { return _HR; } set { _HR = value; } } 如果你想允許你的用戶像這樣個人化某個屬性,你只需要在當前頁面的EditorZone控件中添加一個PropertyGridEditorPart部件就可以了。圖6顯示了當你這樣做的時候,用戶會看到什么:
圖6 Editor 部件允許用戶個人化 Web 部件
如果你定義的某個Web部件屬性是字符串或者數(shù)字類型的,PropertyGridEditorPart 部件會提供一個文本框讓用戶修改屬性值。如果這個屬性是布爾類型的,PropertyGridEditorPart部件會提供一個 復選框,如圖6所示。
圖7給出了從 Windows SharePoint 服務Web組件開發(fā)中學來的一個極好的編程技巧:你可以定義一個基于枚舉類型的個人化屬性。
創(chuàng)建 WebBrowsable 以及基于枚舉類型的個人化的屬性,其真正價值在于 PropertyGridEditorPart 部件會生成一個包括所有可選屬性值的下拉列表,正如圖7中顯示的 Timeframe 屬性那樣。對用戶來說,這很方便,而且有助于保證用戶選擇一個有效的屬性值。
關于 Timeframe 屬性,還有一件事值得留意。它被加上了一個 Personalizable 特性,特性的值為PersonalizationScople.Shared。當屬性像這樣被定義為共享屬性的時候,它只能被自定義,而不可以個人化。既然共享屬性不能被個人化,當當前頁面在用戶范圍的時候,PropertyGridEditorPart 部件不會顯示這個屬性,而只會在共享范圍顯示這個屬性。
Web部件目錄
我們已經(jīng)見過了如何在 WebPartZones 控件中事先放入Web部件。你還可以用另外一種方法完成這個功能,那就是允許用戶在運行時添加新的Web部件。通過使用 CatalogZone 控件和 CatalogParts 類型的部件,比如 :PageCatalogPart 和 DeclarativeCatalogPart 來達到這個目的。(參見
圖8)
當你用這個方法添加某個 CatalogZone 和 CatalogPart 之后,用戶就可以在運行時動態(tài)添加Web部件了,界面就像如圖9中所示的那樣。
圖9:CatalogZones允許用戶動態(tài)添加Web部件
首先,你需要理解 PageCatalogPart 的用途。在設計顯示模式或者編輯顯示模式下,用戶能調(diào)用Web部件的“關閉”命令。當用戶關閉 某個Web部件時,Web部件和相對應的個人化或者自定義配置被保留下來,以便用戶可以在以后再次添加該Web部件。因此,PageCatalogPart 控件顯示所有已經(jīng)被關閉的Web部件的列表,用戶可以再次加到頁面上去。
“關閉”命令與“刪除”命令是不同的!皠h除”命令在出現(xiàn)在編輯顯示模式。當用戶刪除一個Web部件時,有關這個部件的所有相關信息,包括自定義和個人化數(shù)據(jù),全部會被刪除掉。
DeclarativeCatalogPart 部件能夠以聲明的方式添加Web部件。圖8中的代碼說明了怎樣定義這個目錄,其中使用了一個自定義名字的部件 WeatherWebPart。采用這種方法,你就能夠給用戶提供各種各樣的Web部件了。
作為本文的補充,同時為了給你提供更多的有關建立Web部件頁面的詳細信息,我們建議大家去閱讀 Stephen Walther 所寫的“Introducing the ASP.NET 2.0 Web Parts Framework”一文 。文中提供了更詳細的信息,以及使用 EditorZones 和 CatalogZones 建立Web控件頁面的完整例子。Stephen 還講了一些更高級的話題,包括 Verbs、Connections 和 Web 部件的導入導出等內(nèi)容。
ASP.NET 2.0 門戶應用開發(fā)
除了Web部件自身體系結構之外,在ASP.NET 2.0里面還有一些新的特性使得內(nèi)部門戶網(wǎng)站的開發(fā)更加有吸引力。正如文章前面所說,主題和換膚的引入使得風格屬性可以方便直接地獨立于門戶頁面,不用修改每個頁面,就可以做 統(tǒng)一的風格改變。當然,更令人興奮的是母版頁(Master Pages)的引入。使用母版頁,就可以將總體的 WebPartManager 控件和所有 WebPartZones 控件放在一個單獨的模版頁面,其它頁面都可以繼承這些基礎外觀和功能。在示例門戶應用中,我們用到的一個有趣的技術就是在 母版頁每個 WebPartZone 控件的ZoneTemplate 模版中都添加一個 ContentPlaceholder 控件。這樣的話,使用這個 母版頁的內(nèi)容頁面可以使用這個內(nèi)容控件來添加其自己的Web部件,并且映射到相應的 ContentPlaceholder 控件中。
為一個門戶網(wǎng)站設計母版頁時,你必須考慮的一件事是如果給用戶提供自定義特性。正如你已經(jīng)看到的,根據(jù)頁面中包含的不同類型的區(qū)控件,有幾種不同的修改頁面的自定義模式 供讓用戶選擇。
其中一種給用戶顯示自定義選項的方法是將 WebPartManager 控件和一系列按鈕(典型的是LinkButton)封裝為一個用戶控件,然后將這個控件放在 母版頁中,就可以為網(wǎng)站中所有頁面提供自定義選項。如果你的網(wǎng)站有多于一個的主控頁面,用來給不同的頁面提供不同的布局,封裝為一個用戶控件(我們可以叫它WebPartManagerPanel)也是很有用的。之前圖1中給出的那個門戶應用的菜單條就給出了一個示例的用戶控件,可以顯示出當前 WebPartManager 的顯示模式和范圍,還提供了一些 LinkButton 將頁面改變 為 WebPartManager 控件支持的其中一種編輯模式。(這就是我們示例門戶應用使用的用戶控件)。
在你的 WebPartManagerPanel 控件中可以提供的另外一個有用的特性是可以根據(jù)當前用戶和當前頁面顯示或者隱藏相應的顯示模式菜單項。通過查看 WebPartManager 的 SupportedDisplayModes 這個collection屬性中包含哪些支持的顯示模式,就可以顯示或隱藏相應的顯示模式菜單項。例如,要找出當前頁面是否支持CatalogDisplayMode,你應該寫如下的代碼:
if (WebPartManager1.SupportedDisplayModes.Contains( WebPartManager.CatalogDisplayMode))
{
//enable catalog display mode LinkButton here...
}
還應該注意,如果當前用戶沒有相應的權限,調(diào)用 ToggleScope 將會失敗。所以通過代碼判斷一下是否顯示或隱藏讓用戶進入共享范圍的界面元素是個好主意,查詢 WebPartManager 控件的 Personalization 屬性的 CanEnterSharedScope 屬性可以做到這件事。代碼如下:
if (WebPartManager1.Personalization.CanEnterSharedScope){// display UI element that allows user to enter shared scope} 本文附帶的示例應用中的 WebPartManagerPanel 用戶控件包含了一個完整的實現(xiàn),它可以根據(jù)當前用戶和當前頁面的能力動態(tài)地調(diào)整 窗格的顯示。
將用戶控件作為Web部件
在使用 Windows SharePoint 服務創(chuàng)建Web部件的時候,最令人沮喪的一件事情就是你必須用代碼去創(chuàng)建控件的整個界面,設計器一點幫不了忙。因為 許多Web部件都是一系列互相交互的服務器端控件組成的,在創(chuàng)建Web部件時,不能使用 Visual Studio 的設計器是一件很不幸的事情。一個顯而易見的解決方法就是允許開發(fā)人員創(chuàng)建用戶控件,并且可以作為Web部件使用。(一個叫 SmartPart 的第三方工具提供了在 Windows SharePoint服務中可以將用戶控件作為Web部件使用)。
ASP.NET 2.0 Web部件解決了這個問題,它可以允許任何控件直接作為Web部件使用,不用修改或者包裝這些控件。這不僅可以將用戶控件 結合到Web部件集合中,而且還可以輕易地將現(xiàn)有 asp.net 頁面中使用的那些自定義控件集成起來。
這種方法內(nèi)部的工作原理是,如果一個標準控件(不是Web部件)被加入到 WebPartZone 控件中,系統(tǒng)會隱含地調(diào)用 WebPartManager.CreateWebPart 方法,這個方法會創(chuàng)建一個 GenericWebPart 類的實例,并且用 添加的那個控件去初始化這個實例。GenericWebPart 從基類 WebPart 中繼承,提供了核心Web部件屬性實現(xiàn)。當構建 GenericWebPart 控件的時候,它會將初始化的那個控件作為子控件加入。在頁面呈現(xiàn)過程中,就像大多數(shù)復合控件那樣,GenericWebPart自身不會在 響應緩存中輸出任何內(nèi)容,只是作為輸出子控件內(nèi)容的一個代理。最終結果是你可以在頁面中的 WebPartZone 控件里面加入任何控件,不用擔心它不會運行。例如,下面的頁面定義了一個 WebPartZone 控件,里面包括一個用戶控件和一個標準日歷控件,在創(chuàng)建的時候,這兩個控件都會被隱含地包裝成為一個 GenericWebPart 類的控件。代碼如下:
<%@ Register Src="webparts/CustomerList.ascx"TagName="CustomerList" TagPrefix="Wingtip" %><asp:WebPartManager ID=" WebPartManager1" runat="server" /><asp:WebPartZone ID="WebPartZone1" runat="server" HeaderText="Zone 1"><ZoneTemplate> <Wingtip:CustomerList runat="server" id="CustomerList" /> <asp:Calendar runat="server" id="CustomerCalendar" /></ZoneTemplate></asp:WebPartZone> 和標準的Web部件一樣,動態(tài)創(chuàng)建被 GenericWebPart 包裝的控件也是可以的。如果是用戶控件,首先,你必須調(diào)用 Page.LoadControl 來動態(tài)地載入和創(chuàng)建用戶控件實例。其次,還必須顯式地給這個控件設置一個唯一的ID。再者,你還必須調(diào)用 WebPartManager 對象的 CreateWebPart 方法去創(chuàng)建一個 GenericWebPart 類的實例來作為用戶控件實例的包裝。最后,將獲得的 GenericWebPart 實例的引用作為參數(shù)傳給 AddWebPart 方法,并且指定要加入的WebPartZone。代碼如下:
// create Web Part instance from User Control fileControl uc = this.LoadControl(@"webparts\CompanyNews.ascx");uc.ID = "wp2";GenericWebPart wp2 = WebPartManager1.CreateWebPart(uc);WebPartManager1.AddWebPart(wp2, WebPartZone1, 1); 這種技術的唯一缺點就是你無法控制Web部件的一些專用特性,因為你的控件不是從 WebPart 類繼承的,而只有 GenericWebPart 是從 WebPart 類繼承的。一旦你運行擁有由 GenericWebPart 包裝的控件的頁面,你馬上就會很明顯地發(fā)現(xiàn)一個現(xiàn)象,不想大多數(shù)Web部件,這些Web部件默認是無標題的,而且也沒有相關的圖標和描述信息。圖10給出了一個 由 GenericeWebPart 控件包裝的帶有默認標題(無標題)和圖標的示例用戶控件。
圖10 GenericWebPart
其中一種解決方法是,在你的用戶控件中,增加一個Init事件處理例程。如果你的控件由 GenericWebPart包裝(通過查詢 Parent 屬性的類型可以判斷),你就應該在程序中設置 GenericWebPart 類的一些屬性,代碼如下:
void Page_Init(object src, EventArgs e)
{
GenericWebPart gwp = Parent as GenericWebPart;
if (gwp != null){
gwp.Title = "My custom user control";
gwp.TitleIconImageUrl = @"~\img\ALLUSR.GIF";
gwp.CatalogIconImageUrl = @"~\img\ALLUSR.GIF";
}
} 當你再次運行此頁面時,一旦用戶控件被 GenericWebPart 包裝,對 GenericeWebPart 父控件的屬性的修改會反映在包含你的控件的Web部件上。圖11給出了新的設置過屬性的用戶控件,請注意標題和圖標。
圖11 標題和圖標
另外一個更有吸引力的解決方案是直接在你的用戶控件類里實現(xiàn) IWebPart 接口。既然用戶控件從來不直接查詢Web部件的屬性,因為那些信息是由 GenericWebPart 類處理的,這樣做初看起來好像沒什么幫助。幸運的是,GenericWebPart 類的設計者意識到這種需求,如果控件實現(xiàn)了 IWebPart 接口, 那么在 GenericeWebPart 類中實現(xiàn)屬性就會自動委托所包裝的控件。
所以定制某個用戶控件的Web部件特性僅僅是實現(xiàn) IWebPart 接口,并填充接口中定義的七個屬性就可以了。
圖12中的代碼給出了用戶控件的 后臺代碼類的一個例子,實現(xiàn)了和之前我們動態(tài)修改 GenericWebPart 屬性一樣的結果。
你可能還會考慮給你的用戶控件建立一個另外的基類,這個基類從 UserControl 繼承,并且實現(xiàn)了IWebPart接口,然后就可以被你的門戶應用中所有的用戶控件所繼承。我們在這篇文章的示例應用中就是這么做的。采用這種方法,你的用戶控件就能在 它們的構造函數(shù)中初始化其所需的屬性,其它的就由基類去控制了。
圖13給出了一個實現(xiàn) IWebPart 接口的用戶控件基類以及一個與之相對應的后臺類的代碼, 該用戶控件使用這個基類設置標題和圖標屬性。
現(xiàn)在你擁有了創(chuàng)建用戶控件的這么多的靈活性,你可能會問:當你擁有設計器支持的用戶控件,同時還可以定制Web部件特性,那為什么還要創(chuàng)建自己自定義 的控件呢?實際上,有幾個原因需要你這樣做。其中一個原因是你不能給用戶控件添加定義的動作(verbs)。如果需要那樣做,你必須直接從 WebPart 繼承,然后重寫Verbs屬性。當然,你也可以考慮在你的控件中實現(xiàn) IWebEditable 接口。
另外一個原因是用戶控件局限于應用程序的目錄,除非你將.ascx文件從一個項目復制到另外一個項目的目錄下,否則你不可能在多個Web應用程序中共享用戶控件。另一方面,自定義Web部件類繼承自 WebPart 類,能夠被編譯到一個可重用的dll里面, 并且部署到全局程序集緩存(GAC)。還有一點,通過自定義Web部件類,你還可以給你的控件寫一個自定義的設計器,以改變在 Visual Studio 中默認的外觀,而且你還可以在這個Web部件類被放在工具箱的時候,創(chuàng)建一個圖標。
圖14提供了一個特性對比表,讓你決定是選擇自定義Web部件還是用戶控件。
Web部件和個人化特性提供者程序
提供者程序是ASP.NET 2.0的一個新特性,這也是你能在這個版本中看到如此之多內(nèi)置的功能完整的控件只需要很少的甚至不需要任何代碼就能運行 的一個主要原因。提供者程序背后的基本思路是為某個特定的特性定義一套公共的與數(shù)據(jù)相關的任務,將那些任務聚集到一個抽象類聲明中,該抽象類從公共的 ProviderBase 類繼承。在本文探討的個人化特性中,必須明確提供的數(shù)據(jù)相關任務包括:
- 為某個特定頁面和用戶保存Web部件的屬性和布局;
- 為某個特定頁面和用戶裝載Web部件的屬性和布局;
- 保存常規(guī)Web部件屬性和特定的頁面布局( 用于常規(guī)定制);
- 加載常規(guī)Web部件屬性和特定的頁面布局(用于常規(guī)定制);
- 將某個特定頁面和用戶的Web部件屬性和布局重置為其默認值 ;
- 將某個特定頁面的Web部件屬性和布局重置為其默認值(用于常規(guī)定制);
還有其它的一些屬于個人化體系結構的附屬特性也需要持久化存儲的能力,但是基本上可以歸結為以上六種需求。如果我們假設有一個類可以完成這六個動作,而且能夠成功 地保存和恢復數(shù)據(jù),那么當站點運行的時候,每個頁面上的 WebPartManager 控件就能夠使用那個類保存和恢復所有的個人化和自定義數(shù)據(jù)。定義這些方法的抽象類的名字叫 PersonalizationProvider 類, 默認情況下使用的一個具體的派生類是 SqlPersonalizationProvider 類。
圖15顯示了代表我們定義的 六個功能的那三個方法。請注意,不論輸入的 userName 參數(shù)是否為空,每個方法都能夠完成用戶個人化或者共享自定義數(shù)據(jù)的功能。
所有的個人化數(shù)據(jù)都保存為普通的二進制數(shù)據(jù)(byte[]),默認的 SqlPersonalizationProvider 類會將這些數(shù)據(jù)寫入數(shù)據(jù)庫中的一個image類型的字段。既然 ASP.NET 2.0 知道有一個類可以提供這些方法,它就能夠在基礎的控件集里面建立比以前更加多的邏輯。在我們的 案例中,每個使用Web部件的頁面上的 WebPartManager類負責正確地調(diào)用當前的 PersonalizationProvider 類來序列化和恢復每個頁面的個人化設置。圖16展示了 EditorZone 控件與默認的 SqlPersonalizationProvider 類是如何交互的。
圖16 交互
你使用 ASP.NET 2.0 越多,對該提供者架構的例子了解就會越多。比如其中有成員提供者、角色管理提供者、站點地圖提供者、站點監(jiān)控提供者等等很多的提供者,所有的提供者程序都定義了一個相似的核心方法集與控件交互。
修改個人化數(shù)據(jù)存儲
和大多數(shù)ASP.NET 2.0中的提供者程序一樣,默認的個人化提供者程序是面向 SQL Server 后臺存儲而實現(xiàn)的。如果不修改配置文件,默認的 SqlPersonalizationProvider 采用 SQL Server 2005 Express Edition 連接字符串,支持基于本地文件的數(shù)據(jù)庫。這個連接字符串就像下面這樣:
data source=.\SQLEXPRESS; Integrated Security=SSPI; AttachDBFilename=|DataDirectory|aspnetdb.mdf; User Instance=true
使用 SQL Server 2005 Express Edition 基于文件的數(shù)據(jù)庫的一個優(yōu)勢就是它可以被動態(tài)創(chuàng)建,不需要用戶任何附加的設置。這意味著你可以建立一個全新的站點,不用設置數(shù)據(jù)庫就能啟用個人化特性,也能夠運行!當你最初與網(wǎng)站交互時,系統(tǒng)會在站點的 App_Data 目錄中生成一個新的 aspnetdb.mdf 文件,并且用支持所有默認提供者程序所需的表和存儲過程來初始化該數(shù)據(jù)庫。
對于不需要擴展規(guī);蛘咧С趾芏嗖l(fā)用戶的小站點來說,這簡直太好了。但是對于企業(yè)系統(tǒng)來說,需要將數(shù)據(jù)存儲到某個被全面管理的、專用的數(shù)據(jù)庫服務器上。幸運的是,修改 SqlPersonalizationProvider 使用的數(shù)據(jù)庫是非常簡單直接的。SqlPersonalizationProvider 的配置將連接字符串初始化為 LocalSqlServer,這意味著它會在配置文件的<connectionStrings>節(jié)中尋找名字為 LocalSqlServer 的配置項,使用相關的連接字符串去打開到數(shù)據(jù)庫的連接。默認情況下,這個字符串就是你在前文所看到的,意味著它會寫入一個本地的 SQL Server 2005 Express Edition .mdf 文件。要修改它,你必須首先清除掉 LocalSqlServer 連接字符串集合,在你的 Web.config 文件中重新設置一個新的連接字符串值。(或者你也可以修改機器范圍的 Machine.config文件,去影響這臺機器上的所有站點)以下是一個 Web.config 文件的例子,它將提供者數(shù)據(jù)庫的值修改為指向一個本地的 SQL Server 2000 的實例:
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/V.2.0">
<connectionStrings> <clear />
<add name="LocalSqlServer" connectionString="server=.;integrated security=sspi;database=aspnetdb"/>
</connectionStrings>...
</configuration>
在修改生效之前,本地 SQL 服務器上必須有一個名為 aspnetdb 的數(shù)據(jù)庫,里面有SqlPersonalizationProvider 所需的表和存儲過程。隨 ASP.NET 2.0 發(fā)行了一個名為 aspnet_regsql.exe 的工具,用它可以建立這個數(shù)據(jù)庫。當以默認設置運行該程序時,它將創(chuàng)建一個名為 aspnetdb 的本地數(shù)據(jù)庫,其中有所有提供者程序所必需的表和存儲過程,或者你可以選擇將這些表和存儲過程安裝到一個已有的數(shù)據(jù)庫中。所有的表和存儲過程的名字都會以“aspnet”開頭,所以不太可能會與任何現(xiàn)有的表重復。
和所有 ASP.NET 2.0 的提供者程序一樣,這種間接的模式提供了一種非常靈活的架構,使得不用修改任何頁面或者Web部件,就可以將后端的數(shù)據(jù)存儲完全替換掉。
創(chuàng)建自己的個人化提供者程序
為個人化提供者程序修改連接字符串的能力賦予了你一定程度的靈活性,但是在 SqlPersonalizationProvider 內(nèi)部還是使用命名空間 System.Data.Sql.Client 的功能來存取數(shù)據(jù)。這意味著你必須使用 SqlServer 數(shù)據(jù)庫。如果你需要將個人化數(shù)據(jù)保存到另外一種數(shù)據(jù)庫中,或者可能是另外一種完全不同的數(shù)據(jù)存儲中,你將不得不更進一步,創(chuàng)建你自己定制的個人化數(shù)據(jù)提供者。幸運的是,大多數(shù)困難的工作已經(jīng)為你做好了,而且也易于使用。作為個人化數(shù)據(jù)存儲到另外一種數(shù)據(jù)存儲的例子,本文的示例門戶網(wǎng)站有一個自定義提供者程序的完整實現(xiàn),名為 FileBasedPersonalizationProvider 類,它將所有的個人化和自定義數(shù)據(jù)保存到應用程序 App_Data 目錄下的一個本地二進制文件中。這個二進制文件的名稱為每個用戶和路徑唯一生成,每個路徑下還有一個唯一的通用的用戶配置文件。
創(chuàng)建一個自定義的個人化數(shù)據(jù)提供者,你必須首先建立一個從 PersonalizationProvider 基類繼承的新的類,然后重寫所有從基類繼承的抽象方法。
圖17中給出的類定義演示了如何做到這一點。
為了使你的提供者程序能夠運作,其實只有兩個重要的方法必須要實現(xiàn):LoadPersonalizationBlobs 和 SavePersonalizationBlob。這兩個方法完成了個人化數(shù)據(jù)的二進制序列化功能,當加載頁面時,個人化架構會調(diào)用它們。當Web部件頁面處于編輯、目錄或者設計模式時,如果數(shù)據(jù)被修改了,個人化架構還會調(diào)用它們將數(shù)據(jù)寫回。(典型情況下是基于一個特定的用戶)。
在下載的示例代碼中,SavePersonalizationBlob 的實現(xiàn)代碼將 dataBlob 參數(shù)寫入一個基于傳入的用戶名稱和路徑唯一命名的文件。相似地,LoadPersonalizationBlobs 的實現(xiàn)代碼會查找這個文件(使用相同的命名方法),并返回一個用戶個人化的或者共享的blob數(shù)據(jù)。如果傳入的userName參數(shù)為空,這兩個方法默認都會保存或者裝載共享數(shù)據(jù),如果不為空,就會保存或者裝載用戶個人化數(shù)據(jù)。
圖18給出了示例 FileBasedPersonalizationProvider 中這兩個方法的實現(xiàn)代碼,以及一對用來根據(jù)用戶名和路徑信息生成唯一文件名的 helper 方法。
一旦提供者程序完全實現(xiàn)了,通過個人化配置節(jié)中的提供者節(jié),你就可以將這個程序注冊為一個提供者程序。要想實際使用它,你必須在 Web.config 文件中定義它為默認的個人化提供者。下面是一個將我們自定義的基于文件的提供者程序作為默認提供者的例子:
<webParts>
<personalization defaultProvider="FileBasedPersonalizationProvider">
<providers>
<add name="FileBasedPersonalizationProvider"type="Wingtip.Providers.FileBasedPersonalizationProvider" />
</providers>
</personalization>
</webParts>
圖19 使用 FileBasedPersonalizationProvider
更進一步
如果你喜歡這篇文章,在ASP.NET 2.0門戶應用中創(chuàng)建Web部件還有大量的東西需要學習。記得從MSDN雜志網(wǎng)站下載本文的示例代碼。在
ASP.NET 2.0 QuickStart tutorials 中也有一些例子可以拿來研究。我們還推薦你去看看
Fredrik Normén的網(wǎng)志,里面有一些有關 ASP.NET 2.0 Web 部件的有趣的例子。
[
本帖最后由 bijinlong 于 2008-5-21 10:31 編輯 ]