In this last section you'll learn how to integrate most of what you've learned about Web Parts into the existing site. You'll follow the same order of the examples proposed in the "Design" section, i.e., starting from implementing the Web Parts, and then ending with modifying the current master page by adding Web Part zones, the WebPartManager, the editors, and the catalogs.
Because you already have the user controls that you want to insert into the Web Part catalog, all you need to do here is add the proper attributes to the controls' properties, and implement the IWebPart interface. You start from this second point: Implement the interface in a new class called BaseWebPart (found in ~/App_Code/BaseWebPart.cs) that also inherits from UserControl:
public class BaseWebPart : UserControl, IWebPart { private string _catalogIconImageUrl = ""; public string CatalogIconImageUrl { get { return _catalogIconImageUrl; } set { _catalogIconImageUrl = value; } } private string _description = ""; public string Description { get { return _description; } set { _description = value; } } protected string _subTitle = ""; public string Subtitle { get { return _subTitle; } set { _subTitle = value; } } protected string _title = ""; public string Title { get { return _title; } set { _title = value; } } private string _titleIconImageUrl = ""; public string TitleIconImageUrl { get { return _titleIconImageUrl; } set { _titleIconImageUrl = value; } } private string _titleUrl = ""; public string TitleUrl { get { return _titleUrl; } set { _titleUrl = value; } } }
Then, you modify the user controls' code-behind class so that they inherit from this class, and then you add the property attributes. Following is the code for the RssReader control; the code for the PollBox control has been omitted because it would be very similar (except for the fact that the properties are editable only in the shared scope):
public partial class RssReader : BaseWebPart { public RssReader() { this.Title = "RSS Reader"; } [Personalizable(PersonalizationScope.User), WebBrowsable, WebDisplayName("Rss Url"), WebDescription("The Url of the RSS feed")] public string RssUrl { ... } [Personalizable(PersonalizationScope.User), WebBrowsable, WebDisplayName("Header Text"), WebDescription("The header's text")] public string HeaderText { ... } [Personalizable(PersonalizationScope.User), WebBrowsable, WebDisplayName("Number of columns"), WebDescription("The grid's number of columns")] public int RepeatColumns { ... } [Personalizable(PersonalizationScope.User), WebBrowsable, WebDisplayName("Moree Url"), WebDescription("The Url of the link pointing to more content")] public string MoreUrl { ... } [Personalizable(PersonalizationScope.User), WebBrowsable, WebDisplayName("More Url"), WebDescription("The text of the link pointing to more content")] public string MoreText { ... } // the control's event handlers and rest of the class goes here... }
Note how the Title property of the IWebPart interface is set with a default value in the control's constructor. You may want do the same with the properties that specify the Web Part's icon for the title bar and the catalog.
Referring to Figure 10-9, you can edit the Template.Master file by adding the various Web Part zone controls inside the <div> containers (remember that we haven't used tables to define the site layout), and the editor and catalog zones and controls at the top of the layout's center content. For clarity, you can isolate the LinkButton controls that activate one of the available display modes in separate user controls, so that you have less code to add to the master page. The PersonalizationManager control is located under the ~/Controls folder with all the others: Its code is not shown in this section because it's exactly the same code shown earlier in the test page, just copied and pasted into the .ascx file, and the same is true for the companion .cs code-behind file. Following is the code added to the master page:
<html> <body> <form id="Main" runat="server"> ... <div id="container"> <div id="container2"> <div id="centercol"> <div id="breadcrumb"> <mb:PersonalizationManager ID="PersonalizationManager1" runat="server" Visible="false" /> <asp:SiteMapPath ID="SiteMapPath1" runat="server" /> </div> <asp:EditorZone ID="EditorZone1" runat="server"Width="100%"> <ZoneTemplate> <asp:AppearanceEditorPart ID="AppearanceEditorPart1" runat="server" /> <asp:BehaviorEditorPart ID="BehaviorEditorPart1" runat="server" /> <asp:LayoutEditorPart ID="LayoutEditorPart1" runat="server" /> <asp:PropertyGridEditorPart ID="PropertyGridEditorPart1" runat="server" Title="Custom Properties" /> </ZoneTemplate> </asp:EditorZone> <asp:CatalogZone ID="CatalogZone1" runat="server" > <ZoneTemplate> <asp:DeclarativeCatalogPart ID="DeclarativeCatalogPart1" runat="server" Title="Site Catalog"> <WebPartsTemplate> <mb:WelcomeBox ID="WelcomeBox1" runat="server" /> <mb:RssReader id="rssReaderGeneric" runat="server" Title="RSS Reader" /> <mb:RssReader id="rssLatestArticles" runat="server" Title="Latest Articles" RssUrl="~/GetArticlesRss.aspx" MoreText="More articles..." MoreUrl="~/BrowseArticles.aspx" /> <mb:RssReader id="rssLatestThreads" runat="server" Title="Latest Threads" RssUrl="~/GetThreadsRss.aspx?SortExpr=LastPostDate DESC" /> <mb:RssReader id="rssMostActiveThreads"runat="server" Title="Most Active Threads" RssUrl="~/GetThreadsRss.aspx?SortExpr=ReplyCount DESC" /> <mb:PollBox id="pollGeneric" runat="server" ShowArchiveLink="False" ShowHeader="False" /> </WebPartsTemplate> </asp:DeclarativeCatalogPart> <asp:PageCatalogPart ID="PageCatalogPart1" runat="server" Title="Local Page Catalog" /> </ZoneTemplate> </asp:CatalogZone> <div id="centercolcontent"> <div style="clear: both; margin-bottom: 5px;"> <asp:WebPartZone ID="CenterZoneTop" Runat="server" HeaderText="Center Zone Top" Width="100%" /> <table border="0" cellpadding="2" cellspacing="0" width="100%"> <tr> <td width="50%"> <asp:WebPartZone ID="CenterZoneTopLeft" Runat="server" HeaderText="Center Zone Top-Left" Width="100%" /> </td> <td width="50%"> <asp:WebPartZone ID="CenterZoneTopRight" Runat="server" HeaderText="Center Zone Top-Right" Width="100%" /> </td> </tr> </table> </div> <asp:ContentPlaceHolder ID="MainContent" runat="server" /> <div style="clear: both; margin-top: 5px;"> <asp:WebPartZone ID="CenterZoneBottom" Runat="server" HeaderText="Center Zone Bottom" Width="100%" /> <table border="0" cellpadding="2" cellspacing="0" width="100%"> <tr> <td width="50%"> <asp:WebPartZone ID="CenterZoneBottomLeft" Width="100%" Runat="server" HeaderText="Center Zone Bottom-Left" /> </td> <td width="50%"> <asp:WebPartZone ID="CenterZoneBottomRight Width="100%" Runat="server" HeaderText="Center Zone Bottom-Right" /> </td> </tr> </table> </div> </div> </div> <div id="rightcol"> <asp:WebPartZone ID="RightZoneTop" Runat="server" HeaderText="Right Zone Top" Width="100%" /> <mb:NewsletterBox id="NewsletterBox1" runat="server" /> <asp:ContentPlaceHolder ID="RightContent" runat="server" /> <asp:WebPartZone ID="RightZoneBottom" Runat="server" HeaderText="Right Zone Bottom" Width="100%" /> </div> </div> <div id="leftcol"> <asp:WebPartZone ID="LeftZoneTop" Runat="server" HeaderText="Left Zone Top" Width="100%" /> <mb:PollBox id="PollBox1" runat="server" HeaderText="Poll of the week" /> <mb:PollBox id="PollBox2" runat="server" HeaderText="More polls" PollID="24" ShowArchiveLink="False" /> <p></p> <asp:ContentPlaceHolder ID="LeftContent" runat="server" /> <asp:WebPartZone ID="LeftZoneBottom" Runat="server" HeaderText="Left Zone Bottom" Width="100%" /> ... </div> </div> ... </form> </body> </html>
Note that the RssReader control has been added four times to the DeclarativeCatalogPart: One is a generic instance that the user will use to consume third-party feeds, while the other three have predefined values for the RssUrl property that refer to some of the feeds created in previous chapters. Also note that no appearance styles are present in the preceding markup code. This is because all those attributes are defined in the Controls.skin file located under each theme folder, as we have already done for the GridView, DetailsView, DataList, and many other controls. Following is the style declaration of the WebPartZone control, which defines the appearance of the Web Part zone, title bar, border, and drop-down menu of each Web Part:
<asp:WebPartZone runat="server" BorderColor="#CCCCCC" Font-Names="Verdana" Padding="0" PartChromeType="TitleOnly"> <PartTitleStyle CssClass="sectiontitle" /> <MenuLabelHoverStyle ForeColor="#FFCC66" /> <EmptyZoneTextStyle Font-Size="0.8em" /> <MenuLabelStyle ForeColor="White" /> <MenuVerbHoverStyle BackColor="#FFFBD6" BorderColor="#CCCCCC" BorderStyle="Solid" BorderWidth="1px" ForeColor="#333333" /> <HeaderStyle Font-Bold="true" Font-Size="8pt" /> <MenuVerbStyle BorderColor="#990000"BorderStyle="Solid" BorderWidth="1px" ForeColor="White" /> <TitleBarVerbStyle Font-Size="0.6em" Font-Underline="False" ForeColor="White" /> <MenuPopupStyle BackColor="#990000" BorderColor="#CCCCCC" BorderWidth="1px" Font-Names="Verdana" Font-Size="0.6em" /> <MinimizeVerb ImageUrl="~/Images/GoDown.gif"></MinimizeVerb> <RestoreVerb ImageUrl="~/Images/GoUp.gif"></RestoreVerb> <EditVerb ImageUrl="~/Images/Edit.gif"></EditVerb> <CloseVerb ImageUrl="~/Images/Close.gif"></CloseVerb> <DeleteVerb ImageUrl="~/Images/Delete.gif" /> </asp:WebPartZone>
In the master page's code-behind you define the EnablePersonalization Boolean property, which makes the PersonalizationManager visible if set to true, but only if the current user is authenticated; otherwise, it will have no effect:
public partial class TemplateMaster : System.Web.UI.MasterPage { private bool _enablePersonalization = false; public bool EnablePersonalization { get { return _enablePersonalization; } set { _enablePersonalization = value; PersonalizationManager1.Visible = ( this.Page.User.Identity.IsAuthenticated && value); } } protected void Page_Load(object sender, EventArgs e) { if (!this.Page.User.Identity.IsAuthenticated) PersonalizationManager1.Visible = false; } }
To enable personalization on a page, e.g., the default.aspx home page, you merely have to reference the master page by means of the @MasterPage directive at the top of the .aspx page, so that a strongly typed Master property will be built at runtime that exposes all the master page's custom properties:
<%@ MasterType VirtualPath="~/Template.master" %>
Then, you handle the Page_Load event and set to true the EnablePersonalization property of the master page:
protected void Page_Load(object sender, EventArgs e) { this.Master.EnablePersonalization = true; }
Figure 10-10 represents the home page while in design mode, with the Welcome Box in minimized state, and three instances of the RssReader control placed on three different Web Part zones in the site's central column.