This chapter is devoted exclusively to the topic of building Web Parts. You learn how to create Web Parts by creating User controls and by creating custom controls that derive from the base WebPart
class.
We also examine the important topic of authorization filters. You learn how to display different sets of Web Parts to different users. For example, you learn how to create Web Parts that only members of the Administrator role can view.
Later in this chapter, custom Web Part verbs are discussed. You learn how to add custom menu items to a Web Part and execute server-side and client-side code when a user selects the menu item.
This chapter also explores how you can add help to Web Parts. You learn how to open a modal dialog box that displays a help page.
For many of the samples to work in this chapter, you must enable authentication for your application. Windows authentication is enabled by default. For more information about authentication, see Chapter 20, “Using the Login Controls.”
Finally, the WebPartManager
class is examined. You learn how to take advantage of this class to programmatically manipulate the Web Parts contained in a page.
Technically, there are two types of Web Parts. You can create a Web Part by adding any ASP.NET control—including any custom control or User control—to a Web Part Zone. Alternatively, you can create a Web Part by building a custom control that derives from the base WebPart
class.
The Web Part Framework handles these two types of Web Parts differently. When you add a standard ASP.NET control to a Web Part Zone, the Web Part Framework wraps the control in a GenericWebPart
control. Wrapping a standard control in a GenericWebPart
adds all the standard Web Part properties to the control.
For example, if you add an Image control to a Web Part Zone, the Web Part Framework automatically creates a new GenericWebPart
control that includes the Image control as its only child control.
If, on the other hand, you create a Web Part by building a custom control that derives from the base WebPart
class, the Web Part Framework doesn’t need to do anything special with the control. The control is already a Web Part.
Because the GenericWebPart
class itself derives from the base WebPart
class, you can do anything with a generic Web Part that you can do with a “True” Web Part with one important limitation. Because a control gets wrapped in a GenericWebPart
at runtime, there are a number of Web Part properties that you cannot set declaratively when working with a generic Web Part.
In this section, you learn how to create simple Web Parts. You also learn how to take advantage of both the core and extended properties supported by Web Part controls.
All authors of programming books are contractually obligated to include at least one Hello World sample in every book that they write. In this section, two Hello World Web Parts illustrate how to create a Web Part with a User control and a custom control derived from the base WebPart
class. In other words, both a Generic Web Part and a True Web Part are created.
The Generic Hello World Web Part is contained in Listing 28.1.
Notice how simple the Generic Hello World Web Part is. The control simply renders a single line of text.
Creating a True Web Part that does the same thing isn’t that much more difficult. The Web Part in Listing 28.2 inherits from the base WebPart
class, making it a True Web Part.
Example 28.2. App_CodeHelloWorldPart.vb
Imports System Imports System.Web.UI Imports System.Web.UI.WebControls.WebParts Namespace myControls ''' <summary> ''' True Web Part ''' </summary> Public Class HelloWorldPart Inherits WebPart Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter) writer.Write("Hello World!") End Sub End Class End Namespace
The class in Listing 28.2 inherits from the base WebPart
class and overrides the RenderContents()
method to display the single line of text.
You must add the file in Listing 28.2 to the App_Code
folder for the control to be automatically compiled.
The page in Listing 28.3 displays both the Generic and True Web Parts.
Example 28.3. ShowHelloWorldPart.aspx
<%@ Page Language="VB" %> <%@ Register TagPrefix="custom" Namespace="myControls" %> <%@ Register TagPrefix="user" TagName="HelloWorldPart" Src="~/HelloWorldPart.ascx" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <style type="text/css"> .column { float:left; width:45%; height:200px; margin-right:10px; border:solid 1px black; background-color: white; } html { background-color:#eeeeee; } </style> <title>Show Hello World Part</title> </head> <body> <form id="form1" runat="server"> <asp:WebPartManager id="WebPartManager1" Runat="server" /> <asp:WebPartZone id="WebPartZone1" CssClass="column" Runat="server"> <ZoneTemplate> <user:HelloWorldPart id="HelloWorldPart1" Runat="server" /> </ZoneTemplate> </asp:WebPartZone> <asp:WebPartZone id="WebPartZone2" CssClass="column" Runat="server"> <ZoneTemplate> <custom:HelloWorldPart id="HelloWorldPart2" Runat="server" /> </ZoneTemplate> </asp:WebPartZone> </form> </body> </html>
After you open the page in Listing 28.3, you’ll see two Web Part Zones. The Generic Hello World Web Part is contained in the first zone and the True Web Part is contained in the second zone (see Figure 28.1).
Notice that both Web Parts are registered at the top of the page. As an alternative to registering Web Parts in each page, you can register the Web Parts once in an application’s web configuration file, as is shown in Listing 28.4.
When you register a User control in the web configuration file, you must place the User control in a different folder than any page that uses it (otherwise, you’ll get an exception). Typically, you should locate all your user controls in a subfolder in your application. In the web configuration file in Listing 28.4, the HelloWorldPart.ascx
file has been moved to a folder named WebParts.
As mentioned earlier, both Generic and True Web Parts support a core set of properties:
AuthorizationFilter
—. Enables you to specify a string that the Web Part Manager can check before adding a Web Part to a page.
CatalogIconImageUrl
—. Enables you to specify the icon displayed for the Web Part when the Web Part is listed in a Catalog Part.
Description
—. Enables you to specify the description displayed for the Web Part as a tooltip when you hover your mouse over the Web Part.
ExportMode
—. Enables you to export a Web Part control’s settings to an XML file. Possible values are All
, None
, and NonSensitiveData
.
Height
—. Enables you to specify the height of a Web Part control.
Subtitle
—. Enables you to add an additional string to the end of the title displayed in a Web Part.
Title
—. Enables you to specify the title of the Web Part displayed, for example, in the title bar.
TitleIconImageUrl
—. The title icon image is displayed next to the title in the title bar.
TitleUrl
—. Enables you to convert the title into a hyperlink that links to a page with more information about the Web Part.
Width
—. Enables you to specify the width of the Web Part.
You can set any of these properties declaratively on any control that you add to a Web Part Zone. For example, you can set the Title and Description properties like this:
<user:HelloWorldPart id="HelloWorldPart1" Title="Hello World" Description="Displays Hello World" Runat="server" />
When working in Source view in Visual Web Developer, you get a green squiggle warning when setting Web Part properties such as the Title
or Description
property for a User control. Technically, you are setting expando properties because the UserControl
class doesn’t know anything about the specialized Web Part properties. You can safely ignore the warning messages.
Alternatively, you can supply any of these properties with default values in the Web Part control itself. You set these properties in different ways depending on whether you are working with a Generic Web Part control or a True Web Part control.
When setting default Web Part property values for a Generic Web Part control, you need to implement the IWebPart
interface. For example, Listing 28.5 contains the code for a Random Quote Web Part.
Example 28.5. RandomQuotePart.ascx
<%@ Control Language="VB" ClassName="RandomQuotePart" %> <%@ Implements Interface="System.Web.UI.WebControls.WebParts.IWebPart" %> <%@ Import Namespace="System.Collections.Generic" %> <script runat="server"> Private _title As String = "Random Quote" Private _titleUrl As String = "~/Help.aspx" Private _description As String = "Displays a random quote" Private _subTitle As String = "User Control Version" Private _catalogIconImageUrl As String = "~/Images/BigRandomQuote.gif" Private _titleIconImageUrl As String = "~/Images/SmallRandomQuote.gif" Public Property Title() As String Implements IWebPart.Title Get Return _title End Get Set(ByVal Value As String) _title = Value End Set End Property Public Property TitleUrl() As String Implements IWebPart.TitleUrl Get Return _titleUrl End Get Set(ByVal Value As String) _titleUrl = Value End Set End Property Public Property Description() As String Implements IWebPart.Description Get Return _description End Get Set(ByVal Value As String) _description = Value End Set End Property Public ReadOnly Property Subtitle() As String Implements IWebPart.Subtitle Get Return _subTitle End Get End Property Public Property CatalogIconImageUrl() As String Implements IWebPart.CatalogIconImageUrl Get Return _catalogIconImageUrl End Get Set(ByVal Value As String) _catalogIconImageUrl = Value End Set End Property Public Property TitleIconImageUrl() As String Implements IWebPart.TitleIconImageUrl Get Return _titleIconImageUrl End Get Set(ByVal Value As String) _titleIconImageUrl = Value End Set End Property Private Sub Page_PreRender() Dim quotes As New List(Of String) quotes.Add("All paid jobs absorb and degrade the mind -- Aristotle") quotes.Add("No evil can happen to a good man, either in life or after death -- Plato") quotes.Add("The only good is knowledge and the only evil is ignorance -- Plato") Dim rnd As New Random() lblQuote.Text = quotes(rnd.Next(quotes.Count)) End Sub </script> <asp:Label id="lblQuote" runat="server" />
Notice that the User control in Listing 28.5 implements the IWebPart
interface. It includes an <%@ Implements %>
directive at the top of the file. Default values are provided for each of the standard Web Part properties.
When working with a True Web Part control, you do not need to implement the IWebPart
interface. Because a True Web Part control derives from the base WebPart
class, and the WebPart
class contains all the properties of the IWebPart
interface, you simply need to override the Web Part properties that you want to modify.
The Web Part in Listing 28.6 also displays a random quotation. However, this control is a True Web Part control.
Example 28.6. App_CodeRandomQuotePart.vb
Imports System Imports System.Collections.Generic Imports System.Web.UI Imports System.Web.UI.WebControls.WebParts Namespace myControls ''' <summary> ''' Displays a random quotation ''' </summary> Public Class RandomQuotePart Inherits WebPart Private _title As String = "Random Quote" Private _titleUrl As String = "~/Help.aspx" Private _description As String = "Displays a random quote" Private _subTitle As String = "True Web Part Version" Private _catalogIconImageUrl As String = "~/Images/BigRandomQuote.gif" Private _titleIconImageUrl As String = "~/Images/SmallRandomQuote.gif" Public Overrides Property Title() As String Get Return _title End Get Set(ByVal Value As String) _title = value End Set End Property Public Overrides Property TitleUrl() As String Get Return _titleUrl End Get Set(ByVal Value As String) _titleUrl = value End Set End Property Public Overrides Property Description() As String Get Return _description End Get Set(ByVal Value As String) _description = value End Set End Property Public Overrides ReadOnly Property Subtitle() As String Get Return _subTitle End Get End Property Public Overrides Property CatalogIconImageUrl() As String Get Return _catalogIconImageUrl End Get Set(ByVal Value As String) _catalogIconImageUrl = value End Set End Property Public Overrides Property TitleIconImageUrl() As String Get Return _titleIconImageUrl End Get Set(ByVal Value As String) _titleIconImageUrl = value End Set End Property Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter) Dim quotes As New List(Of String)() quotes.Add("All paid jobs absorb and degrade the mind -- Aristotle") quotes.Add("No evil can happen to a good man, either in life or after death -- Plato") quotes.Add("The only good is knowledge and the only evil is ignorance -- Plato") Dim rnd As New Random() writer.Write(quotes(rnd.Next(quotes.Count))) End Sub End Class End Namespace
Notice that the class in Listing 28.6 overrides the properties of the base WebPart
class. For example, the RandomQuotePart
class overrides the Title
property of the base WebPart
class to display a default title for the Web Part (see Figure 28.2).
Implementing the IWebPart
interface in a User Control every time you want to supply a default value for a Web Part property quickly gets tedious. Because the IWebPart interface is an interface, you are obligated to implement all the properties included in the interface even if you need to supply a value for only one of the properties.
To make your life easier, you can create a custom base class for every Web Part that you create with a User control. The UserControlWebPartBase
class in Listing 28.7 creates default values for all the core Web Part properties.
Example 28.7. App_CodeUserControlWebPartBase.vb
Imports System Imports System.Web.UI Imports System.Web.UI.WebControls.WebParts ''' <summary> ''' Base Class for User Control Web Parts ''' </summary> Public Class UserControlWebPartBase Inherits UserControl Implements IWebPart Private _title As String = "Untitled" Private _titleUrl As String = String.Empty Private _description As String = String.Empty Private _subTitle As String = String.Empty Private _catalogIconImageUrl As String = String.Empty Private _titleIconImageUrl As String = String.Empty Public Property Title() As String Implements IWebPart.Title Get Return _title End Get Set(ByVal Value As String) _title = Value End Set End Property Public Property TitleUrl() As String Implements IWebPart.TitleUrl Get Return _titleUrl End Get Set(ByVal Value As String) _titleUrl = Value End Set End Property Public Property Description() As String Implements IWebPart.Description Get Return _description End Get Set(ByVal Value As String) _description = Value End Set End Property Public ReadOnly Property Subtitle() As String Implements IWebPart.Subtitle Get Return _subTitle End Get End Property Public Property CatalogIconImageUrl() As String Implements IWebPart.CatalogIconImageUrl Get Return _catalogIconImageUrl End Get Set(ByVal Value As String) _catalogIconImageUrl = Value End Set End Property Public Property TitleIconImageUrl() As String Implements IWebPart.TitleIconImageUrl Get Return _titleIconImageUrl End Get Set(ByVal Value As String) _titleIconImageUrl = Value End Set End Property End Class
After you create the class in Listing 28.7, you can derive all your User Control Web Parts from this class. For example, the MinimalPart
Web Part in Listing 28.8 inherits from the UserControlWebPartBase
class.
The <%@ Control %>
directive includes an Inherits
attribute that causes the User Control to inherit from the base UserControlWebPartBase
class. Notice that you are not obligated to implement all the properties of the IWebPart
interface in the MinimalPart
. Only the Title
and Description
properties are set.
True Web Parts are slightly more powerful than Generic Web Parts. Not all Web Part properties are exposed through the IWebPart
interface.
When deriving a Web Part from the base WebPart
class, you can take advantage of any of the following properties:
AllowClose
—. Enables you to prevent users from closing a Web Part.
AllowConnect
—. Enables you to prevent users from connecting a Web Part.
AllowEdit
—. Enables you to prevent users from editing a Web Part.
AllowHide
—. Enables you to prevent users from hiding a Web Part.
AllowMinimize
—. Enables you to prevent users from minimizing (collapsing) a Web Part.
AllowZoneChange
—. Enables you to prevent users from moving a Web Part.
AuthorizationFilter
—. Enables you to specify a string that the Web Part Manager can check before adding a Web Part to a page.
CatalogIconImageUrl
—. Enables you to specify the icon displayed for the Web Part when the Web Part is listed in a Catalog Part.
ChromeState
—. Enables you to get or set whether a Web Part is currently minimized or maximized. Possible values are Minimized
and Normal
.
ChromeType
—. Enables you to get or set the appearance of a Web Part’s chrome. Possible values are BorderOnly
, Default
, None
, TitleAndBorder
, and TitleOnly
.
ConnectErrorMessage
—. Enables you to get the error message displayed when errors happen while Web Parts are connected.
CreateEditorParts
—. Enables you to associate particular Editor Parts with a Web Part.
Description
—. Enables you to specify the description displayed for the Web Part as a tooltip when you hover your mouse over the Web Part.
ExportMode
—. Enables you to specify whether a Web Part’s settings can be exported to an XML file.
HasSharedData
—. Enables you to determine whether a Web Part has Shared Personalization data.
HasUserData
—. Enables you to determine whether a Web Part has User Personalization data.
Height
—. Enables you to specify the height of a Web Part control.
HelpMode
—. Enables you to specify the type of user interface displayed for help. Possible values are Modal
, Modeless
, and Navigate
.
HelpUrl
—. Enables you to get or set the URL for a help page associated with the Web Part.
Hidden
—. Enables you to get or set whether a Web Part is hidden. When a Web Part is hidden, the Web Part is rendered with the Cascading Style Sheet attribute display:none
.
ImportErrorMessage
—. Enables you to get or set the message displayed when an error occurs during the import process.
IsClosed
—. Enables you to determine whether a Web Part is closed.
IsShared
—. Enables you to determine whether a Web Part is visible to all users of a Web Part page.
IsStandalone
—. Enables you to determine whether a Web Part is visible to only certain users of a Web Part page.
IsStatic
—. Enables you to determine whether a Web Part is declared in a Web Part Zone (as opposed to being added from a Catalog Zone).
Subtitle
—. Enables you to add an additional string to the end of the title displayed in a Web Part.
Title
—. Enables you to specify the title of the Web Part displayed, for example, in the title bar.
TitleIconImageUrl
—. The title icon image is displayed next to the title in the title bar.
TitleUrl
—. Enables you to convert the title into a hyperlink that links to a page with more information about the Web Part.
WebBrowsableObject
—. Enables you to retrieve the object that is edited by an Editor Part.
WebPartManager
—. Enables you to get the WebPartManager
control responsible for tracking a Web Part.
Width
—. Enables you to specify the width of the Web Part.
Verbs
—. Enables you to retrieve the collection of menu items displayed by a Web Part.
Zone
—. Enables you to get the zone that contains a Web Part.
ZoneIndex
—. Enables you to get the position of a Web Part in a zone.
You can override any of these properties when building a True Web Part control. For example, Listing 28.9 contains a Web Part named the IrritatingPart
. The IrritatingPart
control is hidden and cannot be closed or minimized.
Example 28.9. App_CodeIrritatingPart.vb
Imports System Imports System.Web.UI Imports System.Web.UI.WebControls.WebParts Namespace myControls ''' <summary> ''' Hidden Part that cannot be closed ''' or minimized. ''' </summary> Public Class IrritatingPart Inherits WebPart Private _title As String = "Irritating Part" Private _description As String = "This Web Part is irritating" Private _hidden As Boolean = True Private _allowClose As Boolean = False Private _allowMinimize As Boolean = False Public Overrides Property Title() As String Get Return _title End Get Set(ByVal Value As String) _title = Value End Set End Property Public Overrides Property Description() As String Get Return _description End Get Set(ByVal Value As String) _description = Value End Set End Property Public Overrides Property Hidden() As Boolean Get Return _hidden End Get Set(ByVal Value As Boolean) _hidden = Value End Set End Property Public Overrides Property AllowClose() As Boolean Get Return _allowClose End Get Set(ByVal Value As Boolean) _allowClose = Value End Set End Property Public Overrides Property AllowMinimize() As Boolean Get Return _allowMinimize End Get Set(ByVal Value As Boolean) _allowMinimize = False End Set End Property Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter) writer.Write("<h1>You can't get rid of me!</h1>") End Sub End Class End Namespace
If you add the Web Part in Listing 28.9 to a page, then you won’t be able to see the Web Part in Browse Display mode because the Web Part overrides the Hidden property to hide itself. You can only see the IrritatingPart
when a page is in Design or Catalog Display mode. You also cannot close or minimize the Web Part because both these properties are also overridden (see Figure 28.3).
You can view the IrritatingPart
with the ShowIrritatingPart.aspx
page included on the CD that accompanies this book.
Of course, the IrritatingPart
Web Part is not a very realistic sample of when you would use the Hidden
property. When a Web Part is hidden, the Web Part is rendered but not visible. The Cascading Style Sheet display:none
property is set. Using the Hidden
property is useful when you want to add a non-visual Web Part to a page such as a data source Web Part that exposes all the records from the Movies database table. The Hidden Web Part can be connected to Web Parts that aren’t hidden.
You cannot set any of the properties discussed in this section declaratively in the case of a Generic Web Part. Because the controls in a Web Part Zone do not get wrapped in the GenericWebPart
class until runtime, a Generic Web Part is not a Web Part until a page is actually executed.
However, you can set any of these properties programmatically in the case of either a Generic or True Web Part. The GenericWebPart
class derives from the base WebPart
class, so you can do anything with a Generic Web Part that you can do with a True Web Part. The only requirement is that you do it programmatically rather than declaratively.
For example, the page in Listing 28.10 contains three links for minimizing the Web Parts contained in the page (see Figure 28.4).
Example 28.10. MinimizeWebParts.aspx
<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server"> ''' <summary> ''' Minimize All Web Parts ''' </summary> Protected Sub lnkMinimizeAll_Click(ByVal sender As Object, ByVal e As EventArgs) Dim part As WebPart For Each part In WebPartManager1.WebParts part.ChromeState = PartChromeState.Minimized Next End Sub ''' <summary> ''' Minimize Zone 1 Web Parts ''' </summary> Protected Sub lnkMinimizeZone1_Click(ByVal sender As Object, ByVal e As EventArgs) Dim part As WebPart For Each part In WebPartZone1.WebParts part.ChromeState = PartChromeState.Minimized Next End Sub ''' <summary> ''' Minimize Zone 2 Web Parts ''' </summary> Protected Sub lnkMinimizeZone2_Click(ByVal sender As Object, ByVal e As EventArgs) Dim part As WebPart For Each part In WebPartZone2.WebParts part.ChromeState = PartChromeState.Minimized Next End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <style type="text/css"> .column { float:left; width:30%; height:200px; margin-right:10px; border:solid 1px black; background-color: white; } html { background-color:#eeeeee; } </style> <title>Minimize Web Parts</title> </head> <body> <form id="form1" runat="server"> <asp:WebPartManager id="WebPartManager1" Runat="server" /> <asp:LinkButton id="lnkMinimizeAll" Text="Minimize All" Runat="server" OnClick="lnkMinimizeAll_Click" /> <div class="column"> <asp:LinkButton id="lnkMinimizeZone1" Text="Minimize" OnClick="lnkMinimizeZone1_Click" Runat="server" /> <asp:WebPartZone id="WebPartZone1" Runat="server"> <ZoneTemplate> <asp:Label id="Label1" Title="First Part" Text="First Part" Runat="server" /> <asp:Label id="Label2" Title="Second Part" Text="Second Part" Runat="server" /> </ZoneTemplate> </asp:WebPartZone> </div> <div class="column"> <asp:LinkButton id="lnkMinimizeZone2" Text="Minimize" OnClick="lnkMinimizeZone2_Click" Runat="server" /> <asp:WebPartZone id="WebPartZone2" Runat="server"> <ZoneTemplate> <asp:Label id="Label3" Title="Third Part" Text="Third Part" Runat="server" /> <asp:Label id="Label4" Title="Fourth Part" Text="Fourth Part" Runat="server" /> </ZoneTemplate> </asp:WebPartZone> </div> </form> </body> </html>
When you click the Minimize All link, the collection of all the Web Parts in the page is retrieved from the WebPartManager
control, and the ChromeState
property for each Web Part is set to the value Minimized. If you click the Minimize link associated with a particular zone, then the Web Parts are retrieved from the Zone’s WebParts
property and the Web Parts in the selected zone are minimized.
Notice that the ChromeState
property can be set, even for the Generic Web Parts (Labels) in the page. Because the Generic Web Parts are being retrieved from the WebPartManager
and WebPartZone WebParts
collection, the controls in the Web Part Zones have already been wrapped in the GenericWebPart
class that includes all the properties of a True Web Part.
By taking advantage of the WebParts
property exposed by both the WebPartManager
and WebPartZone
controls, you can retrieve information about the Web Parts contained in a page. For example, the User control in Listing 28.11—the WebPartInfo
control—displays several properties of the Web Parts contained in the page.
Example 28.11. WebPartInfo.ascx
<%@ Control Language="VB" ClassName="WebPartInfo" %> <script runat="server"> Sub Page_PreRender() Dim wpm As WebPartManager = WebPartManager.GetCurrentWebPartManager(Page) grdInfo.DataSource = wpm.WebParts grdInfo.DataBind() End Sub </script> <asp:GridView id="grdInfo" AutoGenerateColumns="false" Runat="server"> <Columns> <asp:BoundField HeaderText="Title" DataField="Title" /> <asp:BoundField HeaderText="IsShared" DataField="IsShared" /> <asp:BoundField HeaderText="IsStatic" DataField="IsStatic" /> <asp:BoundField HeaderText="IsClosed" DataField="IsClosed" /> <asp:BoundField HeaderText="HasUserData" DataField="HasUserData" /> <asp:BoundField HeaderText="HasSharedData" DataField="HasSharedData" /> </Columns> </asp:GridView>
You can add the User Control in Listing 28.11 to any page to view the properties of the Web Parts in the page (see Figure 28.5).
When building a Web Part application, you might want to allow different users of the application to see different sets of Web Parts. For example, you might want the president of the company to be able to view the World Domination Plans Web Part, but you wouldn’t want a lowly temporary employee to see the contents of this particular Web Part. On the other hand, you might want everyone in the company to be able to see the Copying Machine Locations Web Part.
You can filter the Web Parts that a user can see on a Web Part page. When you create an authorization filter, only the Web Parts that a person is authorized to view will appear in Web Part Zones and Web Part Catalogs.
You can create an authorization filter in either of two ways. First, you can handle the AuthorizeWebPart
event of the WebPartManager
control class. This event is raised every time that a Web Part is added to the page while the Web Part Framework builds the page. The event argument passed to the event handler for the AuthorizeWebPart
event includes an IsAuthorized
property. You can set this property to the value True
or False
to indicate whether a Web Part is authorized to be displayed.
You can also create an authorization filter by creating a custom WebPartManager
control that inherits from the WebPartManager
control and overrides its IsAuthorized()
method. The IsAuthorized()
method returns a Boolean value. If you return the value False
, then a Web Part fails authorization and it is not added to the page. Using this second way of creating an authorization filter makes sense when you want to apply the same authorization logic to multiple Web Part pages in an application.
Regardless of whether you handle the AuthorizeWebPart
event or you override the IsAuthorized()
method, it is up to you to write the logic for the authorization filter. In the following sections, you’ll learn three ways of filtering Web Parts, depending on the role of the user.
One of the core properties shared by every Web Part is the AuthorizationFilter
property. This property represents a string that you can use when filtering Web Parts. You can assign any string value to this property.
For example, you can use the AuthorizationFilter
property to represent a user role. In that case, in your AuthorizeWebPart
event handler, you can prevent users who are not in the correct role from seeing a Web Part.
In this section, we create three Web Parts: the MissionStatementPart
, the CopyingMachinesLocationPart
, and the WorldDominationPlanPart
. These Web Parts are contained in Listings 28.12, 13, and 14.
Notice that there is nothing special about these Web Parts. They are simply User controls.
Next, because the Web Parts are filtered by user role, roles need to be enabled for the application. One option would be to use Windows Authentication and local Windows groups. This is the default type of authentication enabled for an ASP.NET application and there is nothing wrong with this option. However, this sample uses Forms Authentication and custom roles. It is easier to switch between users when using Forms Authentication, which makes it easier to test the page.
Forms Authentication and custom roles can be enabled with the Web configuration file in Listing 28.15.
Example 28.15. Web.Config
<?xml version="1.0"?> <configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0"> <system.web> <authentication mode="Forms" /> <roleManager enabled="true" /> <membership defaultProvider="MyMembershipProvider"> <providers> <add name="MyMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="LocalSqlServer" requiresQuestionAndAnswer="false" minRequiredNonalphanumericCharacters="0" minRequiredPasswordLength="1" requiresUniqueEmail="false" /> </providers> </membership> </system.web> </configuration>
The configuration file in Listing 28.15 is included on the CD with the name Web.Config_Disabled
so that the configuration file doesn’t interfere with the other samples in this chapter.
The web configuration file in Listing 28.15 enables Forms Authentication and the Role Manager. It also configures the Membership provider so that email addresses and strong passwords are not required when creating a new user. (This makes it easier to create new users in the page.)
Finally, the page in Listing 28.16 uses an authorization filter to display the three Web Parts (see Figure 28.6).
Example 28.16. ShowAuthorizationFilter.aspx
<%@ Page Language="VB" %> <%@ Register TagPrefix="user" TagName="MissionStatementPart" Src="~/MissionStatementPart.ascx" %> <%@ Register TagPrefix="user" TagName="WorldDominationPlansPart" Src="~/WorldDominationPlansPart.ascx" %> <%@ Register TagPrefix="user" TagName="CopyingMachineLocationsPart" Src="~/CopyingMachinesLocationPart.ascx" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server"> ''' <summary> ''' Create the Administrator and Serf roles and ''' create two users named Bill and Tom. ''' </summary> Private Sub Page_Load() If Not Roles.RoleExists("Administrator") Then Roles.CreateRole("Administrator") Roles.CreateRole("Serf") Membership.CreateUser("Bill", "secret") Membership.CreateUser("Tom", "secret") Roles.AddUserToRoles("Bill", New String(){"Administrator", "Serf"}) Roles.AddUserToRole("Tom", "Serf") End If End Sub ''' <summary> ''' Only add a Web Part when the AuthorizationFilter ''' property contains the current user's role or the ''' AuthorizationFilter property is empty. ''' </summary> Protected Sub WebPartManager1_AuthorizeWebPart(ByVal sender As Object, ByVal e As WebPartAuthorizationEventArgs) e.IsAuthorized = User.IsInRole(e.AuthorizationFilter) Or e.AuthorizationFilter = String.Empty End Sub ''' <summary> ''' Hide the menu for unauthorized users ''' </summary> Private Sub Page_PreRender() Menu1.Visible = Request.IsAuthenticated 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 id="Head1" runat="server"> <style type="text/css"> .login { border:solid 1px black; background-color:white; } .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 Authorization Filter</title> </head> <body> <form id="form1" runat="server"> <asp:WebPartManager id="WebPartManager1" OnAuthorizeWebPart="WebPartManager1_AuthorizeWebPart" Runat="server" /> <asp:Login id="Login1" CssClass="login" TitleText="" Orientation="horizontal" DisplayRememberMe="false" Runat="server" /> <asp:Menu id="Menu1" OnMenuItemClick="Menu1_MenuItemClick" Orientation="Horizontal" CssClass="menu" Runat="server"> <Items> <asp:MenuItem Text="Browse" /> <asp:MenuItem Text="Design" /> <asp:MenuItem Text="Catalog" /> </Items> </asp:Menu> <asp:WebPartZone id="WebPartZone1" CssClass="column" Runat="server"> <ZoneTemplate> <user:MissionStatementPart id="MissionStatementPart1" title="Mission Statement" runat="server" /> <user:CopyingMachineLocationsPart id="CopyingMachineLocationsPart1" title="Copying Machine Locations" AuthorizationFilter="Serf" runat="server" /> <user:WorldDominationPlansPart id="WorldDominationPart1" title="World Domination Plans" AuthorizationFilter="Administrator" runat="server" /> </ZoneTemplate> </asp:WebPartZone> <asp:WebPartZone id="WebPartZone2" CssClass="column" Runat="server" /> <asp:CatalogZone id="CatalogZone1" CssClass="column" Runat="server"> <ZoneTemplate> <asp:DeclarativeCatalogPart id="DeclarativeCatalogPart" Runat="server"> <WebPartsTemplate> <user:CopyingMachineLocationsPart id="CopyingMachineLocationsPart1" title="Copying Machine Locations" AuthorizationFilter="Serf" runat="server" /> <user:WorldDominationPlansPart id="WorldDominationPart1" title="World Domination Plans" AuthorizationFilter="Administrator" runat="server" /> </WebPartsTemplate> </asp:DeclarativeCatalogPart> <asp:PageCatalogPart id="PageCatalogPart1" Runat="server" /> </ZoneTemplate> </asp:CatalogZone> </form> </body> </html>
The Page_Load()
method in Listing 28.16 automatically creates two roles named Administrator and Serf. The method also creates two users named Bill and Tom. Bill is associated with both the Administrator and Serf role and Tom is associated with the Serf role.
After you open the page in Listing 28.16, you’ll see only the MissionStatementPart
Web Part because this is the only Web Part that anonymous users can view.
If you log in with the username Tom
and password secret
, then you can see both the MissionStatementPart
and CopyingMachineLocationsPart
Web Parts. Both these Web Parts are visible in both the Web Part Zone and Catalog Zone contained in the page.
Finally, if you log in with the username Bill
and the password secret
, you can see all three Web Parts. All three Web Parts are displayed in both the Web Part Zone and the Catalog Zone.
Notice that the CopyingMachinesLocationPart
and WorldDominationPlansPart
Web Parts are declared with an AuthorizationFilter
attribute. The CopyingMachinesLocationPart
is associated with the Serf user role. The WorldDominiationPlansPart
is associated with the Administrator user role.
The WebPartManager
control has an AuthorizeWebPart
event handler associated with it. The event handler consists of the following line of code:
e.IsAuthorized = User.IsInRole(e.AuthorizationFilter) Or e.AuthorizationFilter = String.Empty
This line of code checks whether the current user is a member of the role represented by a Web Part’s AuthorizationFilter
property. If the user is in the role associated with the Web Part or the Web Part has no role associated with it, then the user is authorized to see the Web Part.
Using the AuthorizationFilter
property is potentially dangerous when you use the property with Generic Web Parts. The problem is that you might forget to add the AuthorizationFilter
attribute when adding a Generic Web Part to a zone. Because the AuthorizationFilter
property is not a member of the IWebPart
interface, you cannot set this property in the User Control itself.
This section explores an alternate method of creating an authorization filter. This method works only with Web Parts created as User Controls. Web Parts will be filtered depending on their paths.
The idea is that to place every Web Part that a member of the Administrator role can see in a folder named Administrator. Furthermore, every Web Part that a member of the Serf role can see will be located in a folder named Serf.
Let’s start by creating the two User Controls contained in Listing 28.17 and 18.
The SerfPart
Web Part is located in the Serf folder and the AdministratorPart
Web Part is located in the Administrator folder.
The page in Listing 28.19 filters the two Web Parts. Different Web Parts are displayed, depending on the role of the current user.
Example 28.19. ShowAuthorizationPath.aspx
<%@ Page Language="VB" %> <%@ Register TagPrefix="user" TagName="AdministratorPart" Src="~/Administrator/AdministratorPart.ascx" %> <%@ Register TagPrefix="user" TagName="SerfPart" Src="~/Serf/SerfPart.ascx" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server"> ''' <summary> ''' Create the Administrator and Serf roles and ''' the Bill and Tom user accounts ''' </summary> Private Sub Page_Load() If Not Roles.RoleExists("Administrator") Then Roles.CreateRole("Administrator") Roles.CreateRole("Serf") Membership.CreateUser("Bill", "secret") Membership.CreateUser("Tom", "secret") Roles.AddUserToRoles("Bill", New String(){"Administrator", "Serf"}) Roles.AddUserToRole("Tom", "Serf") End If End Sub ''' <summary> ''' Filter based on the Web Part User Control path ''' </summary> Protected Sub WebPartManager1_AuthorizeWebPart(ByVal sender As Object, ByVal e As WebPartAuthorizationEventArgs) e.IsAuthorized = User.IsInRole(GetFolder(e.Path)) End Sub ''' <summary> ''' Gets the name of the folder that contains ''' the User Control ''' </summary> Private Function GetFolder(ByVal path As String) As String Dim baseFolder As String = String.Empty Dim parts As String() = path.Split("/"c) If parts.Length > 2 Then baseFolder = parts(1) End If Return baseFolder End Function Private Sub Page_PreRender() Menu1.Visible = Request.IsAuthenticated 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 id="Head1" runat="server"> <style type="text/css"> .login { border:solid 1px black; background-color:white; } .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 Authorization Path</title> </head> <body> <form id="form1" runat="server"> <asp:WebPartManager id="WebPartManager1" OnAuthorizeWebPart="WebPartManager1_AuthorizeWebPart" Runat="server" /> <asp:Login id="Login1" CssClass="login" TitleText="" Orientation="horizontal" DisplayRememberMe="false" Runat="server" /> <asp:Menu id="Menu1" OnMenuItemClick="Menu1_MenuItemClick" Orientation="Horizontal" CssClass="menu" Runat="server"> <Items> <asp:MenuItem Text="Browse" /> <asp:MenuItem Text="Design" /> <asp:MenuItem Text="Catalog" /> </Items> </asp:Menu> <asp:WebPartZone id="WebPartZone1" CssClass="column" Runat="server"> <ZoneTemplate> <user:AdministratorPart id="AdministratorPart1" title="Administrator Part" runat="server" /> <user:SerfPart id="SerfPart1" title="Serf Part" runat="server" /> </ZoneTemplate> </asp:WebPartZone> <asp:WebPartZone id="WebPartZone2" CssClass="column" Runat="server" /> <asp:CatalogZone id="CatalogZone1" CssClass="column" Runat="server"> <ZoneTemplate> <asp:DeclarativeCatalogPart id="DeclarativeCatalogPart" Runat="server"> <WebPartsTemplate> <user:AdministratorPart id="AdministratorPart1" title="Administrator Part" runat="server" /> <user:SerfPart id="SerfPart1" title="Serf Part" runat="server" /> </WebPartsTemplate> </asp:DeclarativeCatalogPart> <asp:PageCatalogPart id="PageCatalogPart1" Runat="server" /> </ZoneTemplate> </asp:CatalogZone> </form> </body> </html>
After you open the page in Listing 28.19, you won’t see any Web Parts. If you log in with the username Tom
and password secret
, you’ll see the SerfPart
Web Part. If you log in with the user name Bill
and password secret
, you’ll see both the SerfPart
and AdministratorPart
Web Part (Bill is a member of both the Administrator and Serf roles).
The AuthorizeWebPart
event handler consists of the following single line of code:
e.IsAuthorized = User.IsInRole( GetFolder(e.Path) )
The GetFolder()
method returns the root folder of the Web Part being authorized. In other words, it returns the name of the folder where the User Control that corresponds to the Web Part is located. If the name of the root folder matches one of the current user’s roles, then the Web Part is authorized and it is displayed in the page.
You can’t filter a True Web Part by its path because a True Web Part doesn’t have a path. However, you can do something similar. Rather than filter a True Web Part by its path, you can filter a True Web Part by its type.
In this section, two Web Part base classes are created, named AdministratorWebPartBase
and SerfWebPartBase
. Anyone who is a member of the Administrator role can see any Web Part that inherits directly from the AdministratorWebPartBase
class, and anyone who is a member of the Serf role can see any Web Part that inherits directly from the SerfWebPartBase
class.
The AdministratorWebPartBase
and SerfWebPartBase
classes are contained in Listing 28.20 and Listing 28.21.
Example 28.20. App_CodeAdministratorWebPartBase.cs
Imports System Imports System.Web.UI.WebControls.WebParts Namespace myControls ''' <summary> ''' Base class for all Web Parts that ''' an Administrator is authorized to see ''' </summary> Public MustInherit Class AdministratorWebPartBase Inherits WebPart End Class End Namespace
Example 28.21. App_CodeSerfWebPartBase.cs
Imports System Imports System.Web.UI.WebControls.WebParts Namespace myControls ''' <summary> ''' Base class for all Web Parts that ''' a member of the Serf role is authorized ''' to see. ''' </summary> Public MustInherit Class SerfWebPartBase Inherits WebPart End Class End Namespace
Now that we have the base classes, we can inherit any number of Administrator and Serf Web Parts from the base classes. Listing 28.22 and Listing 28.23 contain an Administrator Web Part and a Serf Web Part.
Example 28.22. App_CodeAdministratorPart.cs
Imports System Imports System.Web.UI Imports System.Web.UI.WebControls.WebParts Namespace myControls ''' <summary> ''' A Web Part that only an Administrator can see ''' </summary> Public Class AdministratorPart Inherits AdministratorWebPartBase Private _title As String = "Administrator Part" Public Overrides Property Title() As String Get Return _title End Get Set(ByVal Value As String) _title = value End Set End Property Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter) writer.RenderBeginTag(HtmlTextWriterTag.H3) writer.Write("Administrator Part") writer.RenderEndTag() End Sub End Class End Namespace
Example 28.23. App_CodeSerfPart.cs
Imports System Imports System.Web.UI Imports System.Web.UI.WebControls.WebParts Namespace myControls ''' <summary> ''' A Web Part that only a Serf can see ''' </summary> Public Class SerfPart Inherits SerfWebPartBase Private _title As String = "Serf Part" Public Overrides Property Title() As String Get Return _title End Get Set(ByVal Value As String) _title = value End Set End Property Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter) writer.RenderBeginTag(HtmlTextWriterTag.H3) writer.Write("Serf Part") writer.RenderEndTag() End Sub End Class End Namespace
Notice that the AdministratorPart
inherits from the AdministratorWebPartBase
class, and the SerfPart
inherits from the SerfWebPartBase
class. The authorization filter in Listing 28.24 takes advantage of that fact.
Example 28.24. ShowAuthorizationType.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"> ''' <summary> ''' Create the Administrator and Serf roles and ''' the Bill and Tom user accounts. ''' </summary> Private Sub Page_Load() If Not Roles.RoleExists("Administrator") Then Roles.CreateRole("Administrator") Roles.CreateRole("Serf") Membership.CreateUser("Bill", "secret") Membership.CreateUser("Tom", "secret") Roles.AddUserToRoles("Bill", New String(){"Administrator", "Serf"}) Roles.AddUserToRole("Tom", "Serf") End If End Sub ''' <summary> ''' Authorizes a user to see a Web Part when the base type of the Web Part ''' matches one of the user's roles ''' </summary> Protected Sub WebPartManager1_AuthorizeWebPart(ByVal sender As Object, ByVal e As WebPartAuthorizationEventArgs) e.IsAuthorized = User.IsInRole(GetRoleFromType(e.Type)) End Sub ''' <summary> ''' Returns the name of the base class from a type ''' after stripping the text WebPartBase ''' </summary> Private Function GetRoleFromType(ByVal partType As Type) As String Dim typeName As String = partType.BaseType.Name Return typeName.Replace("WebPartBase", String.Empty) End Function Private Sub Page_PreRender() Menu1.Visible = Request.IsAuthenticated 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 id="Head1" runat="server"> <style type="text/css"> .login { border:solid 1px black; background-color:white; } .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 Authorization Type</title> </head> <body> <form id="form1" runat="server"> <asp:WebPartManager id="WebPartManager1" OnAuthorizeWebPart="WebPartManager1_AuthorizeWebPart" Runat="server" /> <asp:Login id="Login1" CssClass="login" TitleText="" Orientation="horizontal" DisplayRememberMe="false" Runat="server" /> <asp:Menu id="Menu1" OnMenuItemClick="Menu1_MenuItemClick" Orientation="Horizontal" CssClass="menu" Runat="server"> <Items> <asp:MenuItem Text="Browse" /> <asp:MenuItem Text="Design" /> <asp:MenuItem Text="Catalog" /> </Items> </asp:Menu> <asp:WebPartZone id="WebPartZone1" CssClass="column" Runat="server"> <ZoneTemplate> <custom:AdministratorPart id="AdministratorPart1" runat="Server" /> <custom:SerfPart id="SerfPart1" runat="server" /> </ZoneTemplate> </asp:WebPartZone> <asp:WebPartZone id="WebPartZone2" CssClass="column" Runat="server" /> <asp:CatalogZone id="CatalogZone1" CssClass="column" Runat="server"> <ZoneTemplate> <asp:DeclarativeCatalogPart id="DeclarativeCatalogPart" Runat="server"> <WebPartsTemplate> <custom:AdministratorPart id="AdministratorPart1" runat="Server" /> <custom:SerfPart id="SerfPart1" runat="server" /> </WebPartsTemplate> </asp:DeclarativeCatalogPart> </ZoneTemplate> </asp:CatalogZone> </form> </body> </html>
Once again, Tom only can see the SerfPart
Web Part, and Bill can see both the SerfPart
and AdministratorPart
Web Parts. The AuthorizeWebPart
event handler consists of the following single line of code:
e.IsAuthorized = User.IsInRole(GetRoleFromType(e.Type))
The GetRoleFromType()
method returns the name of the base type of the current Web Part (and strips the string "WebPartBase"
from the name). If the current user is a member of the role represented by the base type, then the user can see the Web Part.
In the terminology of the Web Part Framework, a menu item displayed by a Web Part is called a verb. In this section, you’ll learn how to extend the standard set of verbs displayed by a Web Part with your own custom verbs. You’ll learn how to create verbs that execute both server-side and client-side code. You’ll also learn how to add verbs to every Web Part that appears in a particular Web Part Zone.
Adding new verbs to a Web Part is easy. You simply need to provide the Web Part with a Verbs
property that returns the collection of verbs that you want the Web Part to display.
Listing 28.25 contains a Generic Web Part that contains a custom Add to Cart
verb.
Example 28.25. ProductPart.ascx
<%@ Control Language="VB" ClassName="ProductPart" %> <%@ Implements Interface="System.Web.UI.WebControls.WebParts.IWebActionable" %> <%@ Import Namespace="System.Collections.Generic" %> <script runat="server"> Private _productName As String Private _productPrice As Decimal Public ReadOnly Property Verbs() As WebPartVerbCollection Implements IWebActionable.Verbs Get Dim menu As New List(Of WebPartVerb) Dim menuItem As New WebPartVerb("AddToCart", AddressOf AddToCart) menuItem.Text = "Add To Cart" menuItem.Description = "Adds item to shopping cart" menuItem.ImageUrl = "~/Images/AddToCart.gif" menu.Add(menuItem) Return New WebPartVerbCollection(menu) End Get End Property Public Sub AddToCart(ByVal s As Object, ByVal e As WebPartEventArgs) Dim wpm As WebPartManager = WebPartManager.GetCurrentWebPartManager(Page) wpm.MoveWebPart(e.WebPart, wpm.Zones("ShoppingCartZone"), 0) End Sub Public Property ProductName() As String Get Return _productName End Get Set (ByVal Value As String) productName = value End Set End Property Public Property ProductPrice() As Decimal Get Return _productPrice End Get Set (ByVal Value As Decimal) productPrice = value End Set End Property </script> <h3><%= _productName %></h3> Price: <%= _productPrice.ToString("c") %>
Notice that the Web Part in Listing 28.25 implements the IWebActionable
interface. It includes an <%@ Implements %>
directive at the top of the file. This interface contains one member: the Verbs
property.
The Verbs
property returns the verbs (menu items) displayed by the Web Part. In the case of the ProductPart
Web Part, a new verb is created, which displays the text Add to Cart
. The verb also displays an image and it is associated with a server-side method named AddToCart()
.
The AddToCart()
method moves the current Web Part into a Web Part Zone named the ShoppingCartZone
. When you select the Add to Cart menu option on the ProductPart
, the Web Part is moved to the Shopping Cart Zone.
Listing 28.26 contains the same ProductPart
implemented as a True Web part.
Example 28.26. App_CodeProductPart.vb
Imports System Imports System.Collections.Generic Imports System.Web.UI Imports System.Web.UI.WebControls.WebParts Namespace myControls Public Class ProductPart Inherits WebPart Private _title As String = "Product Part" Private _productName As String Private _productPrice As Decimal Public Overrides Property Title() As String Get Return _title End Get Set(ByVal Value As String) _title = value End Set End Property Public Overrides ReadOnly Property Verbs() As WebPartVerbCollection Get Dim menu As New List(Of WebPartVerb)() Dim menuItem As New WebPartVerb("AddToCart", AddressOf AddToCart) menuItem.Text = "Add To Cart" menuItem.Description = "Adds item to shopping cart" menuItem.ImageUrl = "~/Images/AddToCart.gif" menu.Add(menuItem) Return New WebPartVerbCollection(menu) End Get End Property Public Sub AddToCart(ByVal s As Object, ByVal e As WebPartEventArgs) Dim wpm As WebPartManager = WebPartManager.GetCurrentWebPartManager(Page) wpm.MoveWebPart(e.WebPart, wpm.Zones("ShoppingCartZone"), 0) End Sub Public Property ProductName() As String Get Return _productName End Get Set(ByVal Value As String) _productName = value End Set End Property Public Property ProductPrice() As Decimal Get Return _productPrice End Get Set(ByVal Value As Decimal) _productPrice = value End Set End Property Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter) writer.RenderBeginTag(HtmlTextWriterTag.H3) writer.Write(_productName) writer.RenderEndTag() writer.Write("Price: {0:c}", _productPrice) End Sub End Class End Namespace
Notice that the class in Listing 28.26 does not implement the IWebActionable
interface. It doesn’t need to implement this interface because the base WebPart
class already includes a Verbs
property.
You can view both versions of the ProductPart
with the page in Listing 28.27 (see Figure 28.7).
Example 28.27. ShowProductPart.aspx
<%@ Page Language="VB" %> <%@ Register TagPrefix="user" TagName="ProductPart" Src="~/ProductPart.ascx" %> <%@ 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 lnkClear_Click(ByVal sender As Object, ByVal e As EventArgs) Dim part As WebPart For Each part In ShoppingCartZone.WebParts WebPartManager1.MoveWebPart(part, ProductZone, 0) Next End Sub Private Sub Page_PreRender() lnkClear.Visible = (ShoppingCartZone.WebParts.Count > 0) End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <style type="text/css"> .column { float:left; width:40%; padding:10px; height:200px; margin-right:10px; border:solid 1px black; background-color: white; } html { background-color:#eeeeee; } </style> <title>Show Product Part</title> </head> <body> <form id="form1" runat="server"> <asp:WebPartManager id="WebPartManager1" Runat="server" /> <div class="column"> <h1>Products</h1> <asp:WebPartZone id="ProductZone" HeaderText="Products" MenuPopupStyle-BackColor="aliceBlue" MenuPopupStyle-BorderStyle="solid" MenuPopupStyle-BorderWidth="1px" MenuPopupStyle-BorderColor="black" MenuPopupStyle-ShadowColor="lightgray" AllowLayoutChange="false" Runat="server"> <ZoneTemplate> <user:ProductPart id="ProductPart1" Title="Product" ProductName="Laptop Computer" ProductPrice="876.23" Runat="server"/> <custom:ProductPart id="ProductPart2" ProductName="Computer Monitor" ProductPrice="500.89" Runat="server"/> </ZoneTemplate> </asp:WebPartZone> </div> <div class="column"> <h1>Shopping Cart</h1> <asp:WebPartZone id="ShoppingCartZone" HeaderText="Shopping Cart" VerbStyle-CssClass="verbs" AllowLayoutChange="false" Runat="server"/> <asp:LinkButton id="lnkClear" Text="Clear Cart" OnClick="lnkClear_Click" runat="server" /> </div> </form> </body> </html>
After you open the page in Listing 28.27, you can select the Add to Cart menu option from either of the two Web Parts to add the Web Part to the Shopping Cart Zone. Notice that the page also contains a Clear Cart link. Clicking this link moves all the Web Parts back from the Shopping Cart Zone to the Web Part Zone.
A Web Part verb can execute client-side code as well as server-side code. The client-side code can do anything you want. For example, you can call any JavaScript function that you have defined in your page.
In this section, the ProductPart Web Part is modified so that it displays a confirmation dialog box before adding a Web Part to the Shopping Cart Zone. The modified ProductPart
, named ConfirmProductPart
, is contained in Listing 28.28.
Example 28.28. ConfirmProductPart.ascx
<%@ Control Language="VB" ClassName="ConfirmProductPart" %>
<%@ Implements Interface="System.Web.UI.WebControls.WebParts.IWebActionable" %>
<%@ Import Namespace="System.Collections.Generic" %>
<script runat="server">
Private _productName As String
Private _productPrice As Decimal
Public ReadOnly Property Verbs() As WebPartVerbCollection Implements IWebActionable.Verbs
Get
Dim menu As New List(Of WebPartVerb)()
Dim menuItem As New WebPartVerb("AddToCart", AddressOf AddToCart, "return confirm('Are You Sure?'),")
menuItem.Text = "Add To Cart"
menuItem.Description = "Adds item to shopping cart"
menuItem.ImageUrl = "~/Images/AddToCart.gif"
menu.Add(menuItem)
Return New WebPartVerbCollection(menu)
End Get
End Property
Public Sub AddToCart(ByVal s As Object, ByVal e As WebPartEventArgs)
Dim wpm As WebPartManager = WebPartManager.GetCurrentWebPartManager(Page)
wpm.MoveWebPart(e.WebPart, wpm.Zones("ShoppingCartZone"), 0)
End Sub
Public Property ProductName() As String
Get
Return _productName
End Get
Set (ByVal Value As String)
_productName = value
End Set
End Property
Public Property ProductPrice() As Decimal
Get
Return _productPrice
End Get
Set (ByVal Value As Decimal)
_productPrice = value
End Set
End Property
</script>
<h3><%= _productName %></h3>
Price: <%= _productPrice.ToString("c") %>
The ConfirmProductPart
is identical to the ProductPart
that was created in the previous section, except for the one line of code that is highlighted in bold in Listing 28.28. A little bit of JavaScript code has been added, which opens a confirmation dialog box whenever someone selects the Add to Cart menu option (see Figure 28.8).
You can view the ConfirmProductPart
by opening the ShowConfirmProductPart.aspx
page included on the CD that accompanies this book.
If your JavaScript code returns the value false
, then the server-side code has not executed. Therefore, if you click the Cancel button in the JavaScript confirmation dialog box, the server-side AddToCart()
method is not executed.
One problem with the ProductPart
Web Part is that it displays the Add to Cart menu item no matter in what zone the Web Part is displayed. In particular, the Web Part displays this menu option even when the Web Part is already located in the Shopping Cart Zone.
There is a way to fix this problem. Rather than create a custom verb at the level of an individual Web Part, you can create a custom verb at the level of a Web Part Zone. When you create a zone verb, every Web Part contained in the zone gets the additional verb automatically.
The page in Listing 28.29 handles the CreateVerbs
event associated with the Product Zone to add a verb to every Web Part in the zone.
Example 28.29. ShowZoneVerbs.aspx
<%@ Page Language="VB" %> <%@ Import Namespace="System.Collections.Generic" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server"> ''' <summary> ''' Create the verbs that will appear for every ''' Web Part in the Product Zone ''' </summary> Protected Sub ProductZone_CreateVerbs(ByVal sender As Object, ByVal e As WebPartVerbsEventArgs) Dim menu As New List(Of WebPartVerb)() Dim menuItem As New WebPartVerb("AddToCart", AddressOf AddToCart, "return confirm('Are You Sure?'),") menuItem.Text = "Add To Cart" menuItem.Description = "Adds item to shopping cart" menuItem.ImageUrl = "~/Images/AddToCart.gif" menu.Add(menuItem) e.Verbs = New WebPartVerbCollection(e.Verbs, menu) End Sub ''' <summary> ''' Move a Web Part from the Product Zone ''' to the Shopping Cart Zone ''' </summary> Public Sub AddToCart(ByVal s As Object, ByVal e As WebPartEventArgs) Dim wpm As WebPartManager = WebPartManager.GetCurrentWebPartManager(Page) wpm.MoveWebPart(e.WebPart, wpm.Zones("ShoppingCartZone"), 0) End Sub Protected Sub lnkClear_Click(ByVal sender As Object, ByVal e As EventArgs) Dim part As WebPart For Each part In ShoppingCartZone.WebParts WebPartManager1.MoveWebPart(part, ProductZone, 0) Next End Sub Private Sub Page_PreRender() lnkClear.Visible = (ShoppingCartZone.WebParts.Count > 0) End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <style type="text/css"> .column { float:left; width:40%; padding:10px; height:200px; margin-right:10px; border:solid 1px black; background-color: white; } html { background-color:#eeeeee; } </style> <title>Show Zone Verbs</title> </head> <body> <form id="form1" runat="server"> <asp:WebPartManager id="WebPartManager1" Runat="server" /> <div class="column"> <h1>Products</h1> <asp:WebPartZone id="ProductZone" OnCreateVerbs="ProductZone_CreateVerbs" HeaderText="Products" MenuPopupStyle-BackColor="aliceBlue" MenuPopupStyle-BorderStyle="solid" MenuPopupStyle-BorderWidth="1px" MenuPopupStyle-BorderColor="black" MenuPopupStyle-ShadowColor="lightgray" AllowLayoutChange="false" Runat="server"> <ZoneTemplate> <asp:Label id="Label1" Title="Product" Text="Laptop Computer -- $900.00" Runat="server" /> <asp:Label id="Label2" Title="Product" Text="Computer Monitor -- $900.00" Runat="server" /> <asp:Label id="Label3" Title="Product" Text="Network Card -- $64.20" Runat="server" /> </ZoneTemplate> </asp:WebPartZone> </div> <div class="column"> <h1>Shopping Cart</h1> <asp:WebPartZone id="ShoppingCartZone" HeaderText="Shopping Cart" VerbStyle-CssClass="verbs" AllowLayoutChange="false" Runat="server"/> <asp:LinkButton id="lnkClear" Text="Clear Cart" OnClick="lnkClear_Click" runat="server" /> </div> </form> </body> </html>
After you open the page in Listing 28.29, you should notice that each of the Web Parts displayed in the Product Zone includes the Add to Cart menu option. However, as soon as you move a Web Part to the Shopping Cart Zone, this menu option goes away.
When you create True Web Parts, you have the option of adding a help menu option to the Web Part. You can set two properties related to help:
HelpUrl
—. The URL of the help page to display.
HelpMode
—. The user interface used when displaying the help page. Possible values are Modal
, Modeless
, and Navigate
.
If you set HelpMode
to the value Modal
, then the help page opens in a modal window that you must close before returning to the original Web Part page. This option works only with Internet Explorer because it uses the Microsoft proprietary ShowModalDialog()
statement.
The Web Part in Listing 28.30 displays a modal help page (see Figure 28.9).
Example 28.30. App_CodeConfusingPart.vb
Imports System Imports System.Web.UI Imports System.Web.UI.WebControls.WebParts Namespace myControls ''' <summary> ''' This Web Part illustrates how to ''' use the HelpUrl and HelpMode properties ''' </summary> Public Class ConfusingPart Inherits WebPart Private _title As String = "Confusing Part" Private _helpUrl As String = "~/ConfusingPartHelp.aspx" Private _helpMode As WebPartHelpMode = WebPartHelpMode.Modal Public Overrides Property Title() As String Get Return _title End Get Set(ByVal Value As String) _title = value End Set End Property Public Overrides Property HelpUrl() As String Get Return _helpUrl End Get Set(ByVal Value As String) _helpUrl = value End Set End Property Public Overrides Property HelpMode() As WebPartHelpMode Get Return _helpMode End Get Set(ByVal Value As WebPartHelpMode) _helpMode = value End Set End Property Protected Overrides Sub RenderContents(ByVal writer As HtmlTextWriter) writer.RenderBeginTag(HtmlTextWriterTag.H1) writer.Write("This Web Part is confusing") writer.RenderEndTag() writer.Write("Select the Help menu option for help") End Sub End Class End Namespace
Notice that the Web Part in Listing 28.30 overrides both the HelpUrl
and HelpMode
properties.
This final section of this chapter takes a closer look at the WebPartManager
control. The WebPartManager
control is responsible for tracking all the Web Parts in a page. It exposes the primary application programming interface for working with Web Parts.
The WebPartManager
control supports the following particularly useful properties (this is not a complete list):
AvailableTransformers
—. Represents all the transformers available on the page. Transformers are used when you connect incompatible Web Parts.
Connections
—. Represents all the connections between Web Parts in a page.
DisplayMode
—. Represents the current Display mode.
DisplayModes
—. Returns a collection of all the Display modes associated with the WebPartManager
.
Personalization
—. Represents the WebPartPersonalization
class, which tracks personalization data.
SelectedWebPart
—. Represents the Web Part that is currently selected on the page. For example, when you select a Web Part control’s Edit menu option, the Web Part becomes the selected Web Part.
SupportedDisplayModes
—. Represents all the Display modes supported by the current page,
WebParts
—. Represents all the Web Parts on the page,
Zones
—. Represents of the Web Part Zones on the page,
Some of these properties require additional explanation. Both the DisplayModes
and the SupportedDisplayModes
properties return a collection of Web Part Display Modes. However, in some situations, the DisplayModes
property returns more Display Modes than the SupportedDisplayModes
property. For example, if a page does not include a Catalog Zone, then the SupportedDisplayModes
property doesn’t return CatalogDisplayMode
as one of its values.
You can take advantage of the SupportDisplayModes
property to automatically populate a Menu control with the list of available Display Modes like this:
Sub Page_Load() If Not Page.IsPostBack Then For Each mode As WebPartDisplayMode in WebPartManager1.SupportedDisplayModes Menu1.Items.Add(new MenuItem(mode.Name)) Next End If End Sub
The Zones property represents all the WebPartZone
controls on a page. However, it does not include tool zones such as Catalog or Editor Zones.
The WebPartManager
control also supports a number of useful methods:
AddWebPart
—. Enables you to add a new Web Part to a Web Part Zone.
CanConnectWebParts
—. Enables you to determine whether two Web Parts can be connected in a page. This method optionally enables you to specify a Transformer for the connection.
CloseWebPart
—. Enables you to close a Web Part.
ConnectWebParts
—. Enables you to connect two Web Parts in a page. This method optionally enables you to specify a Transformer for the connection.
CreateWebPart
—. Enables you to create a new Generic Web Part from a control.
DeleteWebPart
—. Enables you to delete a Web Part from a page.
DisconnectWebParts
—. Enables you to break the connection between two Web Parts.
ExportWebPart
—. Enables you to export Web Part settings to XML.
GetConsumerConnectionPoints
—. Enables you to get all the consumer connection points exposed by a Web Part.
GetCurrentWebPartManager
—. Enables you to get a reference to a page’s WebPartManager
control from any user control or component used in a page. This is a shared (static) method.
GetGenericWebPart
—. Enables you to retrieve an existing Web Part as a Generic Web Part.
GetProviderConnectionPoints
—. Enables you to get all the provider connection points exposed by a Web Part.
ImportWebPart
—. Enables you to import Web Part settings from XML.
IsAuthorized
—. Enables you to create an authorization filter.
MoveWebPart
—. Enables you to move a Web Part between Web Part Zones, or change the position of a Web Part in a Web Part Zone.
Notice that everything that you can do from particular tool zones—such as Editor and Catalog Zones—you can do using the methods of the WebPartManager
control.
For example, the page in Listing 28.31 illustrates how you can dynamically rearrange the Web Parts in a Web Part Zone every time the page is requested.
Example 28.31. DynamicWebParts.aspx
<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server"> Private Sub Page_PreRender() Dim rnd As Random = New Random() Dim webPartToMove As WebPart = WebPartManager1.WebParts(rnd.Next(WebPartManager1.WebParts.Count)) Dim NewZoneIndex As Integer = rnd.Next(webPartToMove.Zone.WebParts.Count) WebPartManager1.MoveWebPart(webPartToMove, webPartToMove.Zone, NewZoneIndex) End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head id="Head1" runat="server"> <style type="text/css"> .column { width:400px; height:200px; margin-right:10px; border:solid 1px black; background-color: white; } html { background-color:#eeeeee; } </style> <title>Dynamic Web Parts</title> </head> <body> <form id="form1" runat="server"> <asp:WebPartManager id="WebPartManager1" Runat="server" /> <asp:WebPartZone id="FeaturedProductsZone" CssClass="column" Runat="server"> <ZoneTemplate> <asp:Panel id="FeaturedBooks" Title="Featured Books" Runat="server"> <ul> <li>Blink</li> <li>A Theory of Justice</li> </ul> </asp:Panel> <asp:Panel id="FeaturedMovies" Title="Featured Movies" Runat="server"> <ul> <li>Star Wars: Episode III</li> <li>Blade Runner</li> </ul> </asp:Panel> <asp:Panel id="FeaturedMusic" Title="Featured Music" Runat="server"> <ul> <li>Black Eyed Peas</li> <li>Coldplay</li> </ul> </asp:Panel> </ZoneTemplate> </asp:WebPartZone> </form> </body> </html>
In Listing 28.31, the MoveWebPart()
method called in the Page_Load()
event handler is used to randomly move one of the Web Parts in the Featured Products Zone to a new position (see Figure 28.10).
Finally, the WebPartManager
control supports a number of useful events:
AuthorizeWebPart
—. Raised when the WebPartManager
control adds each Web Part to a page (and Web Parts are displayed in the Catalog Zone).
ConnectionsActivated
—. Raised after a connection between two Web Parts is activated.
ConnectionsActivating
—. Raised before a connection between two Web Parts is activated.
DisplayModeChanged
—. Raised after the Web Part Display Mode is changed.
DisplayModeChanging
—. Raised before the Web Part Display Mode is changed.
SelectedWebPartChanged
—. Raised after a Web Part is selected (for example, selected for editing).
SelectedWebPartChanging
—. Raised before a Web Part is selected (for example, selected for editing).
WebPartAdded
—. Raised after a new Web Part is added to the page.
WebPartAdding
—. Raised before a new Web Part is added to the page.
WebPartClosed
—. Raised after a Web Part is closed.
WebPartClosing
—. Raised before a Web Part is closed.
WebPartDeleted
—. Raised after a Web Part is deleted.
WebPartDeleting
—. Raised before a Web Part is deleted.
WebPartMoved
—. Raised after a Web Part is moved.
WebPartMoving
—. Raised before a Web Part is moved.
WebPartsConnected
—. Raised after a Web Part is connected.
WebPartsDisconnected
—. Raised after a Web Part is disconnected.
WebPartsDisconnecting
—. Raised before a Web Part is disconnected.
Notice that most of these events come in pairs. There is a DisplayModeChang
ing
event and a DisplayModeChang
ed
event. You can cancel the action associated with most of these events within the ing
event handler.
The page in Listing 28.32 handles the WebPartClosed
event to display an informational message after a user closes a Web Part.
Example 28.32. ShowCloseWarning.aspx
<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server"> Protected Sub WebPartManager1_WebPartClosed(ByVal sender As Object, ByVal e As WebPartEventArgs) divCloseWarning.Visible = True 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 id="Head1" runat="server"> <style type="text/css"> .divCloseWarning { position:absolute; background-color:#eeeeee; padding:15px; left:200px; top:50px; border:double 3px red; width:200px; } .column { float:left; width:40%; height:200px; margin-right:10px; border:solid 1px black; background-color: white; } .menu { margin:5px 0px; } html { background-color:#eeeeee; } </style> <title>Show Close Warning</title> </head> <body> <form id="form1" runat="server"> <asp:WebPartManager id="WebPartManager1" OnWebPartClosed="WebPartManager1_WebPartClosed" Runat="server" /> <asp:Menu id="Menu1" OnMenuItemClick="Menu1_MenuItemClick" Orientation="Horizontal" CssClass="menu" Runat="server"> <Items> <asp:MenuItem Text="Browse" /> <asp:MenuItem Text="Design" /> <asp:MenuItem Text="Catalog" /> </Items> </asp:Menu> <asp:WebPartZone id="FeaturedProductsZone" CssClass="column" Runat="server"> <ZoneTemplate> <asp:Label id="Label1" Title="First Web Part" Text="Contents of First Web Part" Runat="server" /> <asp:Label id="Label2" Title="Second Web Part" Text="Contents of Second Web Part" Runat="server" /> </ZoneTemplate> </asp:WebPartZone> <div id="divCloseWarning" class="divCloseWarning" Visible="false" Enableviewstate="false" Runat="server"> You have closed a Web Part. You can reopen the Web Part by opening the Page Catalog. <br /><br /> <asp:Button id="btnOK" Text="OK" Runat="server" /> </div> <asp:CatalogZone id="CatalogZone1" CssClass="column" runat="server"> <ZoneTemplate> <asp:PageCatalogPart id="PageCatalogPart1" Runat="server" /> </ZoneTemplate> </asp:CatalogZone> </form> </body> </html>
After you open the page in Listing 28.32, you can close a Web Part by selecting a Web Part control’s Close menu option. When you close a Web Part, an informational message appears (see Figure 28.11).
In this chapter, you learned how to build Web Parts. We started by creating two Hello World Web Parts. One Web Part was created with a User Control, and the other Web Part was created with a control that inherits from the base WebPart
class.
Next, you learned how to create authorization filters for Web Parts. You learned different methods of filtering Web Parts so that different users can view different sets of Web Parts on the same page.
You also learned how to add custom verbs (menu items) to a Web Part. You learned how to associate both server-side and client-side code with a verb.
We also discussed the important topic of adding help to a Web Part. You learned how to open a modal dialog box that displays help information for a particular Web Part.
Finally, the Web Part application programming interface exposed by the WebPartManager
control was explored in detail. You learned how to manipulate the Web Parts contained in a page through the methods of the WebPartManager
class.