Creating Custom Catalog Zones


As previously mentioned, the default functionality of a Web Part Catalog is pretty limited. By default, Web Part Catalogs do not support paging or sorting. You also cannot drag and drop new Web Parts from a catalog into a Web Part Zone.

This section explores several methods of creating fancier catalogs. We'll create a catalog that automatically displays all the Web Parts available in the current application, a catalog that supports drag-and-drop, and a catalog that supports templates.

How Catalog Zones Work

Three types of parts in the Web Part Framework are related to catalogs: the CatalogZone class, the CatalogPartChrome class, and the CatalogPart class. All three parts participate in the rendering of a catalog. The CatalogZone class creates an instance of the CatalogPartChrome class, and the CatalogPartChrome class renders each CatalogPart control contained in the Catalog Zone.

It is important to understand that individual CatalogPart controls, such as the DeclarativeCatalogPart or PageCatalogPart control, don't render anything. They merely act as databases of available catalog items.

The CatalogPart class has two important methods:

  • GetAvailableWebPartDescriptions Returns the list of descriptions of Web Parts contained in the catalog.

  • GetWebPart Returns an actual Web Part.

For example, the DeclarativeCatalogPart control derives from the CatalogPart class. This control overrides the GetAvailableWebPartDescriptions() method and returns the list of catalog parts that have been listed in its template. The DeclarativeCatalogPart also overrides the GetWebPart() method to return one of the Web Parts declared in its template.

The individual Catalog Part controls contained in a Catalog Zone do not render anything. The CatalogZone control creates an instance of the CatalogPartChrome control and uses this instance to render each of the CatalogPart controls.

The CatalogPartChrome control has four important methods:

  • CreateCatalogPartChromeStyle() If you override this method, you can modify the properties of the Style object used when rendering the chrome and catalog part.

  • PerformPreRender() If you override this method, you can execute code before a CatalogPart control is rendered.

  • RenderCatalogPart() If you override this method, then you modify the entire rendering behavior of the chrome.

  • RenderPartContents() If you override this method, then you can render additional content inside the chrome.

The CatalogZone class creates an instance of the CatalogPartChrome class and calls its RenderCatalogPart() method to render the selected CatalogPart control. The CatalogZone control itself includes several valuable methods:

  • CreateCatalogParts() Returns the list of catalog parts that the Catalog Zone displays.

  • InvalidateCatalogParts() Resets the collection of catalog parts associated with the Catalog Zone.

  • RenderContents() Renders the entire Catalog Zone.

  • RenderHeader() Renders the header area of a catalog zone.

  • RenderBody() Renders the body area of a catalog zone.

  • RenderFooter() Renders the footer area of a catalog zone.

  • RenderCatalogPartLinks() Renders the list of links to the particular Catalog Parts contained in the Catalog Zone.

  • RenderVerbs() Renders the verbs that appear at the bottom of a catalog zone.

The CatalogZoneBase class also includes the following properties:

  • CatalogParts This property automatically calls the CreateCatalogParts() method to create a list of catalog parts.

  • SelectedCatalogPartID This property enables you to get or set the current Catalog Part.

If you want to modify the appearance of any Catalog Part, then you must modify the rendering behavior of the Catalog Zone or Catalog Part Chrome that contains the Catalog Part.

Creating a Reflection Catalog Part

Let's start by creating a new Catalog Part that automatically lists all the Web Part controls contained in the App_Code folder. Because the custom Catalog Part will take advantage of reflection to determine the list of available Web Parts, we'll name this custom Catalog Part the Reflect Catalog Part.

Note

Reflection refers to the process of retrieving information about .NET Framework typessuch as classes, methods, properties, and attributesat runtime. Two namespaces in the .NET Framework are devoted to reflection: System.Reflection and System.Reflection.Emit.


The ReflectCatalogPart control is contained in Listing 30.11.

Listing 30.11. ReflectCatalogPart.cs

[View full width]

Imports System Imports System.Reflection Imports System.Collections.Generic Imports System.Web.UI.WebControls.WebParts Namespace myControls     ''' <summary>     ''' Catalog Part that automatically displays all     ''' Web Parts defined in the App_Code folder     ''' </summary>     Public Class ReflectCatalogPart         Inherits CatalogPart         Private _catalog As New Dictionary(Of String, WebPart)         ''' <summary>         ''' We create the list of available Web Parts         ''' during the Init event since we use this list         ''' with both the GetAvailableWebPartDescriptions()         ''' and GetWebPart() methods         ''' </summary>         Protected Overrides Sub OnInit(ByVal e As EventArgs)             ' Get the list of Web Parts through reflection             Dim moduleArray As Reflection.Module() = Assembly.GetExecutingAssembly() .GetModules(False)             Dim webPartTypes As Type() = moduleArray(0).FindTypes(AddressOf WebPartFilter,  Nothing)             ' Create and instance of each Web Part and add to catalog             For i As Integer = 0 To webPartTypes.Length - 1                 Dim newPart As WebPart = CType(Activator.CreateInstance(webPartTypes(i)),  WebPart)                 newPart.ID = "part" + i.ToString()                 _catalog.Add(newPart.ID, newPart)             Next             MyBase.OnInit(e)         End Sub         ''' <summary>         ''' Returns a collection of descriptions of Web Parts         ''' in the App_Code folder         ''' </summary>         Public Overrides Function GetAvailableWebPartDescriptions() As  WebPartDescriptionCollection             Dim descriptions As New List(Of WebPartDescription)()             For Each NewPart As WebPart In _catalog.Values                 descriptions.Add(New WebPartDescription(NewPart))             Next             Return New WebPartDescriptionCollection(descriptions)         End Function         ''' <summary>         ''' Because you already instantiated all the Web Parts         ''' in the OnInit() method, you simply return a         ''' Web Part from the catalog collection         ''' </summary>         Public Overrides Function GetWebPart(ByVal description As WebPartDescription) As  WebPart             Return _catalog(description.ID)         End Function         ''' <summary>         ''' You don't want a list of all classes in the App_Code folder,         ''' only those classes that derive from the WebPart class         ''' </summary>         Private Function WebPartFilter(ByVal t As Type, ByVal s As Object) As Boolean             Return GetType(WebPart).IsAssignableFrom(t)         End Function     End Class End Namespace 

The list of Web Parts defined in the App_Code folder is retrieved in the OnInit() method. The current module is retrieved by grabbing the first module in the currently executing assembly. Next, the FindTypes() method is called with a filter that returns only classes derived from the Web Part class (the filter used by FindTypes() is created with the WebPartFilter() method).

The GetAvailableWebPartDescriptions() method returns a collection of WebPartDescription classes. The Catalog Zone calls this method to get the list of Web Parts that it displays. In our implementation of this method, we simply copy the list of Web Parts that we retrieved in the OnInit() method into a WebPartDescriptionCollection class.

Finally, the GetWebPart() method returns a Web Part that matches a description. This method is called by the Catalog Zone when a Web Part is added to a Web Part Zone. Again, we take advantage of the collection built in the OnInit() method to return a Web Part that matches the description parameter.

You can use the custom ReflectCatalogPart in the page in Listing 30.12. You'll need to switch to Catalog Display Mode by clicking the Catalog link to see the Reflect Catalog Part.

Listing 30.12. ShowReflectCatalogPart.aspx

<%@ Page Language="VB" %> <%@ Register TagPrefix="custom" Namespace="myControls" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server">     Protected Sub Menu1_MenuItemClick(ByVal sender As Object, ByVal e As MenuEventArgs)         WebPartManager1.DisplayMode = WebPartManager1.DisplayModes(e.Item.Text)     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <style type="text/css">         .column         {             float:left;             width:30%;             height:200px;             margin-right:10px;             border:solid 1px black;             background-color: white;         }         .menu         {             margin:5px 0px;         }         html         {             background-color:#eeeeee;         }     </style>     <title>Show Reflect Catalog Part</title> </head> <body>     <form  runat="server">     <asp:WebPartManager                  Runat="server" />         <asp:Menu                          OnMenuItemClick="Menu1_MenuItemClick"             Orientation="Horizontal"             Css             Runat="server">             <Items>             <asp:MenuItem Text="Browse" />             <asp:MenuItem Text="Design" />             <asp:MenuItem Text="Catalog" />             </Items>         </asp:Menu>         <asp:WebPartZone                          Css             Runat="server" />         <asp:WebPartZone                          Css             Runat="server" />         <asp:CatalogZone                          Css             Runat="server">             <ZoneTemplate>                 <custom:ReflectCatalogPart                                          Runat="server" />             </ZoneTemplate>         </asp:CatalogZone>     </form> </body> </html> 

When you open the page in Listing 30.12 and click the Catalog link, you'll see all of the Web Parts defined in your App_Code folder listed (see Figure 30.4). The Reflect Catalog Part doesn't display anything unless you add some Web Parts to your App_Code folder. In particular, the control doesn't show Web Parts that you have created with User Controls because these Web Parts are contained in a different assembly.

Figure 30.4. Automatically displaying available Web Parts.


Creating a Drag-and-Drop Catalog Zone

Although the Web Part Framework supports drag-and-drop functionality when you move Web Parts between Web Part Zones, the Web Part Framework does not support drag-and-drop when you want to add new Web Parts to a Web Part Zone from a Catalog Zone. In this section, we'll fix this limitation of the Web Part Framework by creating a custom DragDropCatalogZone control.

To create the custom Catalog Zone, you won't have to do as much work as you might expect. You can leverage the existing JavaScript library included with the Web Part Framework. We'll tweak this library so it will work the same way with Catalog Zones as it works with Web Part Zones.

First, we need to create a custom Catalog Zone. Listing 30.13 contains the code for the custom DragDropCatalogZone class.

Listing 30.13. DragDropCatalogZone.vb

[View full width]

Imports System Imports System.Web.UI Imports System.Web.UI.WebControls.WebParts Namespace myControls     ''' <summary>     ''' CatalogZone that supports drag-and-drop     ''' adding of Web Parts     ''' </summary>     Public Class DragDropCatalogZone         Inherits CatalogZone         ''' <summary>         ''' Adds the client-side script for drag-and-drop         ''' </summary>         Protected Overrides Sub OnPreRender(ByVal e As EventArgs)             EnsureID()             Dim startupScript As String = String.Format("dragDropCatalogZone.start('{0 }');", Me.ClientID)             If Not Page.ClientScript.IsClientScriptIncludeRegistered ("DragDropCatalogZone") Then                 Page.ClientScript.RegisterClientScriptInclude("DragDropCatalogZone", Page .ResolveUrl("~/ClientScripts/DragDropCatalogZone.js"))                 Page.ClientScript.RegisterStartupScript(GetType(DragDropCatalogZone),  "DragDropCatalogZone", startupScript, True)             End If             MyBase.OnPreRender(e)         End Sub         ''' <summary>         ''' Utility method to return currently         ''' selected Catalog Part         ''' </summary>         Public ReadOnly Property SelectedCatalogPart() As CatalogPart             Get                 Return Me.CatalogParts(Me.SelectedCatalogPartID)             End Get         End Property         ''' <summary>         ''' Override the default postback handler to         ''' add support for adding a Web Part to a         ''' Web Part Zone         ''' </summary>         Protected Overrides Sub RaisePostBackEvent(ByVal eventArgument As String)             If eventArgument.StartsWith("Add:") Then                 Dim args() As String = eventArgument.Split(":"c)                 AddWebPart(args(1), args(2), Int32.Parse(args(3)))             End If             MyBase.RaisePostBackEvent(eventArgument)         End Sub         ''' <summary>         ''' Use the WebPartManager control to         ''' actually add the Web Part         ''' </summary>         Private Sub AddWebPart(ByVal webPartId As String, ByVal zoneId As String, ByVal  zoneIndex As Integer)             ' Get Web Part             Dim desc As WebPartDescription = SelectedCatalogPart .GetAvailableWebPartDescriptions()(webPartId)             Dim webPart As WebPart = SelectedCatalogPart.GetWebPart(desc)             ' Get Zone             Dim zone As WebPartZoneBase = Me.WebPartManager.Zones(zoneId)             ' Add Web Part             Me.WebPartManager.AddWebPart(webPart, zone, zoneIndex)         End Sub         ''' <summary>         ''' This Web Part Zone uses the specialized         ''' DragDropWebPartChrome         ''' </summary>         Protected Overrides Function CreateCatalogPartChrome() As CatalogPartChrome             Return New DragDropCatalogPartChrome(Me)         End Function     End Class     ''' <summary>     ''' This class extends the base CatalogPartChrome     ''' class by adding a div element to mark the     ''' area of draggable images.     ''' </summary>     Public Class DragDropCatalogPartChrome         Inherits CatalogPartChrome         ''' <summary>         ''' Add the DIV tag         ''' </summary>         Public Overrides Sub RenderCatalogPart(ByVal writer As HtmlTextWriter, ByVal  catalogPart As CatalogPart)             writer.AddAttribute(HtmlTextWriterAttribute.Id, GenerateId())             writer.RenderBeginTag(HtmlTextWriterTag.Div)             MyBase.RenderCatalogPart(writer, catalogPart)             writer.RenderEndTag()         End Sub         ''' <summary>         ''' Create a unique ID for the DIV tag         ''' so we can grab the DIV tag in our         ''' client script         ''' </summary>         Private Function GenerateId() As String             Return String.Format("{0}_draggable", Me.Zone.ID)         End Function         Public Sub New(ByVal zone As CatalogZoneBase)             MyBase.New(zone)         End Sub     End Class End Namespace 

The custom Catalog Zone does three things. First, the OnPreRender() method adds a reference to an external JavaScript file named DragDropCatalogZone.js. This file contains the JavaScript code used to support the client-side drag-and-drop functionality. This file is discussed in a moment.

The custom Catalog Zone also overrides the base CatalogZone control's RaisePostBackEvent() method. This method is called after a user drops a catalog item into a Web Part Zone. The method adds the new Web Part to the zone with the help of the WebPartManager control's AddWebPart() method.

Finally, the custom Catalog Zone overrides the base CreateCatalogPartChrome() method. Notice that the file in Listing 30.13 actually defines two classes: DragDropCatalogZone and DragDropCatalogPartChrome. The DragDropCatalogZone control renders a catalog part by using the custom chrome defined by the DragDropCatalogPartChrome class. This class simply adds a <div> tag around the contents of the CatalogPart. This <div> tag is used in the JavaScript code to identify the draggable catalog items.

The JavaScript code used by the custom DragDropCatalogZone control is contained in Listing 30.14.

Listing 30.14. DragDropCatalogZone.js

[View full width]

var dragDropCatalogZone = new function()     {         this.start = dragDropCatalogZone_start;         this.addWebPart = dragDropCatalogZone_addWebPart;     } function dragDropCatalogZone_start(catalogZoneId)     {         // Get Catalog Zone         var catalogZone = document.getElementById(catalogZoneId);         // Find Element with Draggable Class         var draggable = document.getElementById(catalogZoneId + '_draggable');         // Get Images contained in Draggable         var images = draggable.getElementsByTagName('img');         // Get Inputs contained in Draggable         var inputs = draggable.getElementsByTagName('input');         // Check that Images == Inputs         if (images.length != inputs.length)         {             alert('DragDropCatalogZone:Each catalog item must have a catalog icon');             return;         }         // Convert images into Web Parts         for (var i=0;i<images.length;i++)         {             var catItem = new WebPart(images[i],images[i]);             images[i].webPartId = inputs[i].value;             images[i].detachEvent("ondragend", WebPart_OnDragEnd);             images[i].attachEvent("ondragend", dragDropCatalogZone.addWebPart);         }         // Add Drop Handler to WebPartManager         __wpm.xCatalogZoneId = catalogZoneId;         __wpm.xCompleteAddWebPartDragDrop = dragDropCatalogZone_CompleteAddWebPartDragDrop;     } function dragDropCatalogZone_addWebPart()     {         __wpm.xCompleteAddWebPartDragDrop();     } function dragDropCatalogZone_CompleteAddWebPartDragDrop() {     var dragState = this.dragState;     this.dragState = null;     if (dragState.dropZoneElement != null) {         dragState.dropZoneElement.__zone.ToggleDropCues(false, dragState.dropIndex, false);     }     document.body.detachEvent("ondragover", Zone_OnDragOver);     for (var i = 0; i < __wpm.zones.length; i++) {         __wpm.zones[i].allowDrop = false;     }     this.overlayContainerElement.removeChild(this.overlayContainerElement.firstChild);     this.overlayContainerElement.style.display = "none";     if ((dragState != null) && (dragState.dropped == true)) {         var currentZone = dragState.webPartElement.__webPart.zone;         var currentZoneIndex = dragState.webPartElement.__webPart.zoneIndex;         if ((currentZone != dragState.dropZoneElement.__zone) ||             ((currentZoneIndex != dragState.dropIndex) &&              (currentZoneIndex != (dragState.dropIndex - 1)))) {             var eventTarget = this.xCatalogZoneId;             var eventArgument = 'Add:' + dragState.webPartElement.webPartId + ':' +  dragState.dropZoneElement.__zone.uniqueID + ':' + dragState.dropIndex;             this.SubmitPage(eventTarget, eventArgument);         }     } } 

The dragDropCatalogZone_start() method is called immediately after the DragDropCatalogZone control is rendered. This method takes advantage of the <div> tag added to the Catalog Zone by the DragDropCatalogPartChrome class to identify the images and input elements associated with the catalog items.

The method converts each of the images in the catalog zone into a Web Part by calling the WebPart() constructor defined in the Web Part Framework JavaScript library. Next, the method overrides the default JavaScript function that is called when a Web Part is dropped into a Web Part Zone. When a user drops a catalog item into a Web Part Zone, the dragDropCatalogZone_CompleteAddWebPartDragDrop() method is called.

The dragDropCatalogZone_CompleteAddWebPartDragDrop() method posts the page back to the server by calling the SubmitPage() method. An eventArgument that represents the Web Part ID, Web Part Zone ID, and Web Part Zone index is posted back to the server and processed by the RaisePostBackEventHandler() method. This server method actually adds the Web Part to the selected zone.

Listing 30.15 illustrates how you can use the DragDropCatalogZone control in a page.

Listing 30.15. ShowDragDropCatalogZone.aspx

<%@ Page Language="VB" %> <%@ Register TagPrefix="custom" Namespace="myControls" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server">     Protected Sub Menu1_MenuItemClick(ByVal sender As Object, ByVal e As MenuEventArgs)         WebPartManager1.DisplayMode = WebPartManager1.DisplayModes(e.Item.Text)     End Sub     </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <style type="text/css">         .column         {             float:left;             width:30%;             height:200px;             margin-right:10px;             border:solid 1px black;             background-color: white;         }         .menu         {             margin:5px 0px;         }         html         {             background-color:#eeeeee;         }     </style>     <title>Show Drag-and-Drop Catalog Zone</title> </head> <body>     <form  runat="server">     <asp:WebPartManager                  Runat="server" />         <asp:Menu                          OnMenuItemClick="Menu1_MenuItemClick"             Orientation="Horizontal"             Css             Runat="server">             <Items>             <asp:MenuItem Text="Browse" />             <asp:MenuItem Text="Design" />             <asp:MenuItem Text="Catalog" />             </Items>         </asp:Menu>         <asp:WebPartZone                          Css             Runat="server" />         <asp:WebPartZone                          Css             Runat="server" />         <custom:DragDropCatalogZone                          Css             Runat="server">             <ZoneTemplate>                 <asp:DeclarativeCatalogPart                                          Runat="server">                     <WebPartsTemplate>                     <asp:Label                                                  CatalogIconImageUrl="~/Images/FirstSimplePart.gif"                         Title="First Simple Part"                         Description="The first Web Part"                         Runat="server" />                     <asp:Label                                                  CatalogIconImageUrl="~/Images/SecondSimplePart.gif"                         Title="Second Simple Part"                         Description="The second Web Part"                         Runat="server" />                     </WebPartsTemplate>                 </asp:DeclarativeCatalogPart>                 <asp:PageCatalogPart                                          Runat="server" />             </ZoneTemplate>         </custom:DragDropCatalogZone>     </form> </body> </html> 

After you open the page in Listing 30.15, you can view the custom Catalog Zone by clicking the Catalog link. Notice that you can add items from the catalog to the page by dragging the catalog icons onto particular Web Part Zones (see Figure 30.5).

Figure 30.5. Dragging and dropping from a catalog.


Web Standards Note

The DragDropCatalogZone control still renders the normal check boxes for adding items. This is good from an accessibility standpoint. When you implement fancy JavaScript drag-and-drop functionality, you should always implement an equivalent way of doing the same thing from the keyboard.


Creating a Templated Catalog Zone

The default CatalogZone control has a certain appearance and there is nothing you can do about it. The CatalogZone control does not support paging or sorting. It doesn't even display the descriptions with Web Parts.

In this section, we'll fix this limitation of the Web Part Framework by building a custom Templated Catalog Zone. The custom CatalogZone control supports an ItemTemplate. You can take advantage of the ItemTemplate to display catalog items in any way that you please.

For example, you can use a GridView control in the ItemTemplate to enable users to sort and page through catalog items. You can also take advantage of the ItemTemplate to modify the default appearance of the Catalog Part menu. Rather than display a list of links to individual Catalog Parts, you can display a drop-down list of Catalog Parts (see Figure 30.6).

Figure 30.6. Displaying a drop-down list of catalog parts.


The custom Templated Catalog Zone control is contained in Listing 30.16.

Listing 30.16. TemplatedCatalogZone.vb

[View full width]

Imports System Imports System.Web.UI Imports System.Web.UI.WebControls.WebParts Namespace myControls     ''' <summary>     ''' This control enables you to lay out a     ''' Catalog Zone with a template     ''' </summary>     Public Class TemplatedCatalogZone         Inherits CatalogZone         Private _itemTemplate As ITemplate         Private _item As ItemContainer         ''' <summary>         ''' Represents the Item Template         ''' </summary>         <TemplateContainer(GetType(TemplatedCatalogZone))> _         Public Property ItemTemplate() As ITemplate             Get                 Return _itemTemplate             End Get             Set(ByVal Value As ITemplate)                 _itemTemplate = Value             End Set         End Property         ''' <summary>         ''' Utility method that returns the currently         ''' selected Catalog Part         ''' </summary>         Public ReadOnly Property SelectedCatalogPart() As CatalogPart             Get                 Return CatalogParts(SelectedCatalogPartID)             End Get         End Property         ''' <summary>         ''' Returns list of Web Web Part descriptions         ''' so that you can use Container.Descriptions         ''' in the ItemTemplate         ''' </summary>         Public ReadOnly Property Descriptions() As WebPartDescriptionCollection             Get                 Return SelectedCatalogPart.GetAvailableWebPartDescriptions()             End Get         End Property         ''' <summary>         ''' Returns list of Web Part Zones so that you         ''' can use Container.Zones in the ItemTemplate         ''' </summary>         Public ReadOnly Property Zones() As WebPartZoneCollection             Get                 Return Me.WebPartManager.Zones             End Get         End Property         ''' <summary>         ''' Adds a new Web Part to a Web Part Zone         ''' </summary>         Public Sub AddWebPart(ByVal webPartID As String, ByVal zoneID As String)             ' Get Web Part             Dim descs As WebPartDescriptionCollection = SelectedCatalogPart .GetAvailableWebPartDescriptions()             Dim NewWebPart As WebPart = SelectedCatalogPart.GetWebPart(descs(webPartID))             ' Get Zone             Dim zones As WebPartZoneCollection = Me.WebPartManager.Zones             Dim selectedZone As WebPartZoneBase = zones(zoneID)             ' Add the Web Part             Me.WebPartManager.AddWebPart(NewWebPart, selectedZone, 0)         End Sub         ''' <summary>         ''' Extends base method to support the ItemTemplate         ''' </summary>         Protected Overrides Sub CreateChildControls()             MyBase.CreateChildControls()             _item = New ItemContainer()             ItemTemplate.InstantiateIn(_item)             Controls.Add(_item)         End Sub         ''' <summary>         ''' Render the contents of the ItemTemplate         ''' </summary>         Protected Overrides Sub RenderBody(ByVal writer As HtmlTextWriter)             _item.RenderControl(writer)         End Sub         ''' <summary>         ''' Suppress the footer since the same content         ''' is contained in our ItemTemplate         ''' </summary>         Protected Overrides Sub RenderFooter(ByVal writer As HtmlTextWriter)         End Sub     End Class     ''' <summary>     ''' This class does nothing but hold the contents     ''' of the ItemTemplate so we can render it.     ''' </summary>     Public Class ItemContainer         Inherits Control     End Class End Namespace 

The TemplatedCatalogZone control in Listing 30.16 inherits from the base CatalogZone class. The derived class exposes an ItemTemplate property that you can use to customize the control's appearance. The ItemTemplate is created in the CreateChildControls() method, and the contents of the ItemTemplate are rendered in the RenderBody() method.

Notice that the TemplatedCatalogZone class includes a public AddWebPart() method. This method adds a Web Part with a particular ID to a particular Web Part Zone. The Web Part is added with the help of the WebPartManager class's AddWebPart() method.

The page in Listing 30.17 contains the TemplatedCatalogZone control.

Listing 30.17. ShowTemplatedCatalogZone.aspx

[View full width]

<%@ Page Language="VB" %> <%@ Register TagPrefix="custom" Namespace="myControls" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server">     ''' <summary>     ''' Whenever the CatalogZone is opened, we need to     ''' rebind to the datasource     ''' </summary>     Protected Sub WebPartManager1_DisplayModeChanged(ByVal sender As Object, ByVal e As  WebPartDisplayModeEventArgs)         If WebPartManager1.DisplayMode Is WebPartManager.CatalogDisplayMode Then             CatalogZone1.SelectedCatalogPartID = CatalogZone1.CatalogParts(0).ID             CatalogZone1.DataBind()         End If     End Sub     ''' <summary>     ''' When a user selects a new Catalog Part,     ''' we need to rebind the GridView     ''' </summary>     Protected Sub btnSelectCatalog_Click(ByVal sender As Object, ByVal e As EventArgs)         ' Update selected catalog         Dim dropCatalogs As DropDownList = CType(CatalogZone1.FindControl("dropCatalogs"),  DropDownList)         CatalogZone1.SelectedCatalogPartID = dropCatalogs.SelectedValue     End Sub     ''' <summary>     ''' When a user selects a new page, we     ''' must rebind the GridView     ''' </summary>     Protected Sub grdDesc_PageIndexChanging(ByVal sender As Object, ByVal e As  GridViewPageEventArgs)         Dim grdDesc As GridView = CType(sender, GridView)         grdDesc.PageIndex = e.NewPageIndex     End Sub     ''' <summary>     ''' When a user selects a Web Part from a Catalog, we     ''' add the new Web Part to the page     ''' </summary>     Protected Sub grdDesc_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)         ' Rebind the GridView to reload the DataKeys         Dim grdDesc As GridView = CType(sender, GridView)         grdDesc.DataBind()         ' Get Selected Web Part         Dim webPartID As String = CType(grdDesc.SelectedValue, String)         ' Get Selected Zone         Dim dropZones As DropDownList = CType(CatalogZone1.FindControl("dropZones"),  DropDownList)         Dim zoneID As String = dropZones.SelectedValue         ' Add the Web Part to the Page         CatalogZone1.AddWebPart(webPartID, zoneID)     End Sub     ''' <summary>     ''' If in Catalog Display Mode, bind GridView     ''' </summary>     Protected Overrides Sub OnPreRenderComplete(ByVal e As EventArgs)         If WebPartManager1.DisplayMode Is WebPartManager.CatalogDisplayMode Then             Dim grdDesc As GridView = CType(CatalogZone1.FindControl("grdDesc"), GridView)             grdDesc.DataBind()         End If         MyBase.OnPreRenderComplete(e)     End Sub     Protected Sub Menu1_MenuItemClick(ByVal sender As Object, ByVal e As MenuEventArgs)         WebPartManager1.DisplayMode = WebPartManager1.DisplayModes(e.Item.Text)     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <style type="text/css">         .catalogPartStyle         {             padding:10px;             font:14px Georgia,serif;         }         .catalogPartStyle fieldset         {             padding-top:30px;             padding-bottom:10px;             margin-bottom:10px;         }         .catalogPartStyle label         {             font-weight:bold;         }         .catalogGrid         {             margin:10px;         }         .catalogRow td         {             padding:5px;             border-bottom:solid 1px black;         }         .column         {             float:left;             width:30%;             height:200px;             margin-right:10px;             border:solid 1px black;             background-color: white;         }         .menu         {             margin:5px 0px;         }         html         {             background-color:#eeeeee;         }     </style>     <title>Show Templated Catalog Zone</title> </head> <body>     <form  runat="server">     <asp:WebPartManager                 Runat="server" OnDisplayModeChanged="WebPartManager1_DisplayModeChanged" />         <asp:Menu                          OnMenuItemClick="Menu1_MenuItemClick"             Orientation="Horizontal"             Css             Runat="server">             <Items>             <asp:MenuItem Text="Browse" />             <asp:MenuItem Text="Design" />             <asp:MenuItem Text="Catalog" />             </Items>         </asp:Menu>         <asp:WebPartZone                          HeaderText="Zone 1"             Css             Runat="server" />         <asp:WebPartZone                          HeaderText="Zone 2"             Css             Runat="server"/>         <custom:TemplatedCatalogZone                          Css             Runat="server">             <ItemTemplate>             <fieldset>             <legend>Select Catalog</legend>             <asp:DropDownList                                  DataTextField="Title"                 DataValueField="ID"                 DataSource='<%# Container.CatalogParts %>'                 runat="server"/>             <asp:Button                                  Text="select"                 Tooltip="Select Catalog"                 OnClick="btnSelectCatalog_Click"                 runat="server"/>             </fieldset>             <asp:Label                                  Text="Add to Zone:"                 AssociatedControl                 Runat="server"/>             <asp:DropDownList                                  DataTextField="HeaderText"                 DataValueField="ID"                 DataSource='<%# Container.Zones %>'                 runat="server" />             <asp:GridView                                  GridLines="none"                 Css                 RowStyle-Css                 DataKeyNames="ID"                 AllowPaging="true"                 PageSize="2"                 ShowHeader="false"                 EmptyDataText="This catalog is empty"                 AutoGenerateColumns="false"                 AutoGenerateSelectButton="true"                 DataSource='<%# Container.Descriptions %>'                 OnPageIndexChanging="grdDesc_PageIndexChanging"                 OnSelectedIndexChanged="grdDesc_SelectedIndexChanged"                 runat="server">                 <Columns>                     <asp:BoundField DataField="Title" />                     <asp:BoundField DataField="Description" />                 </Columns>              </asp:GridView>             </ItemTemplate>             <ZoneTemplate>             <asp:DeclarativeCatalogPart                                  Title="Declarative Catalog"                 Runat="server">                 <WebPartsTemplate>                     <asp:Label                                                  Title="Label 1"                         Description="The very first Label"                         Runat="server" />                     <asp:Label                                                  Title="Label 2"                         Description="The second Label"                         Runat="server" />                     <asp:Label                                                  Title="Label 3"                         Description="The very last Label"                         Runat="server" />                 </WebPartsTemplate>             </asp:DeclarativeCatalogPart>             <asp:PageCatalogPart                                  Title="Page Catalog"                 Runat="server"/>             </ZoneTemplate>         </custom:TemplatedCatalogZone>     </form> </body> </html> 

If you open the page in Listing 30.17 in your browser and click the Catalog link, you can see the custom Templated Catalog Zone. Notice that the custom Catalog Zone supports paging. Furthermore, notice that unlike the default CatalogZone control, the list of Catalog Parts is displayed in a drop-down list.

In Listing 30.17, the TemplatedCatalogZone control contains an ItemTemplate that includes two DropDownList controls and a GridView control. The DropDownList controls are used to display a list of Catalog Parts and a list of Web Part Zones. The GridView control is used to display the list of catalog items (the Web Part descriptions).

Each of these three controls is bound to a datasource with a databinding expression. For example, the DropDownList control that displays the list of Catalog Parts is declared like this:

<asp:DropDownList          DataTextField="Title"     DataValueField="ID"     DataSource='<%# Container.CatalogParts %>'     runat="server"/> 


In this control declaration, Container.CatalogParts refers to the CatalogParts property exposed by the TemplateCatalogControl class. Because this class also exposes a Descriptions and Zones property, you can also use Container.Descriptions and Container.Zones in a databinding expression within the TemplateCatalogZone control's ItemTemplate.

Because the controls in the ItemTemplate do not take advantage of declarative databinding, you must undertake the responsibility of binding the controls to their respective data sources. Notice, for instance, that the GridView control is bound to its data source in the OnPreRenderComplete() method defined near the top of the file in Listing 30.17.

Note

Notice that the GridView is bound to its data source during the PreRenderComplete event. This is the last event that happens before the page is rendered to the browser. You must perform the databinding at this point in the page execution lifecycle because certain Catalog Parts, such as the PageCatalogPart control, don't update their catalog of Web Parts until the PreRender event.


The advantage of using the TemplatedCatalogZone control is that you can make a Catalog Zone look like anything you want. However, as Spiderman says, "With great power comes great responsibility." The disadvantage is that you are forced to write all sorts of additional code to bind the data controls to their data sources.




ASP. NET 2.0 Unleashed
ASP.NET 2.0 Unleashed
ISBN: 0672328232
EAN: 2147483647
Year: 2006
Pages: 276

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net