Chapter 5. Making Web Parts customizable – SharePoint 2010 Web Parts in Action

Chapter 5. Making Web Parts customizable

 

 

One of the key features of Web Parts is that the user can customize them. The Web Part infrastructure was designed with this in mind; it provides several features to aid the developer. Web Parts and other controls in ASP.NET have properties that can be used to control the logic, appearance, and behavior. These properties are normally set in the code by the programmer, but Web Parts can be exposed and customized using the web interface.

This chapter shows how you as a developer can expose the properties to your end users so that they can customize or personalize them. You’ll learn the default Web Part properties; you’ll add your own properties and eventually create a custom interface for interacting with them. Properties can be simple types, such as strings or integers, which SharePoint supports. For advanced types and properties, there’s no out-of-the-box support for editing, which means that you need to implement the logic yourself for those.

SharePoint Server 2010 also includes support for targeting Web Parts to groups and audiences. I’ll show you how this works behind the scenes and explain how to implement a similar targeting behavior for SharePoint Foundation.

Apart from the actual user interface of the Web Part, the editing of the properties is where your Web Part meets the end user. It’s therefore a necessity to have the enduser experience in mind when making your Web Parts customizable.

5.1. Web Part properties

A Web Part is based on an ASP.NET control and has properties that can be set by the developer. Compared to regular ASP.NET web controls, the Web Part has properties that are marked with special attributes that allow them to be editable in the web interface. Height and Width are good examples of such properties. You can set the default values of these properties and then the users can modify them using the web interface. The Web Part infrastructure and SharePoint contain the functionality to automatically create an interface for editing the Web Part properties and then save these values. This means that for simple properties you don’t need to do anything other than mark the property as customizable.

To understand how Web Parts properties can be customizable, let’s start by building a Web Part for which you’ll create a few customizable properties. You’ll see how to add various attributes to control the customization scope, categorization, and storage of the property values. These properties will use the Web Part infrastructure and the automatically generated web interface for editing them. To make the Web Parts user friendly, you should specify default values, and I’ll show you a few options for doing just that. You’ll also investigate the problems that might occur with Web Part properties when you’re using Visual Web Parts or when you’re using your Web Parts as static Web Parts.

To begin, let’s use a custom Web Part that reads information from an RSS feed and presents it to the user. This RSS Web Part will initially have the following customizable properties:

  • The RSS feed URL
  • The number of items to show

Our RSS Web Part will be built in such a way that the properties can be customized using either the web interface or SharePoint Designer 2010. You’ll use Web Parts based on the ASP.NET Web Part implementation for the sample of this chapter. But I’ll also show you the differences in the Web Part properties when using the SharePoint implementation of Web Parts.

5.1.1. Adding a property

Adding a property to a Web Part involves a few simple steps. First, you define the actual property in the class by creating a public read and write property. Then, you have to decorate this property with Web Part–specific attributes so that the Web Part infrastructure can determine which properties will be available to the user.

To create the RSS Web Part, create a new project in Visual Studio using the Empty Project template and then add a Web Part item to that project. Give the Web Part the name of RssWebPart. You should select Farm Solution for this project, because later you’ll use functionality not available in the sandbox. Once you’ve set up your project, navigate to and open the Web Part code file.

The first property you’ll add is the URL to the RSS feed. This is a string property; call it RssFeedUrl and make it public. Your code should look like this when you’re done:

public string RssFeedUrl {
    get;
    set;
}

Note that you’re using the automatic getter and setter notation, introduced in C# 3.0, just to make the code look cleaner. If you deploy your Web Part after just adding this public property and then choose to customize the Web Part, you won’t see the property in the Web Part tool pane. This is because you haven’t yet told SharePoint that this is a customizable property. The property would still be available if you created the Web Part programmatically, or declaratively in an ASPX page. For example, you could use the following code snippet in an ASPX page in SharePoint using SharePoint Designer, which would instantiate the Web Part and set the value of the property:

<webparts:RssWebPart
    runat="server"
    id="rssWebPart"
    RssFeedUrl="http://feeds2.feedburner.com/WictorWilen"/>

To further customize this Web Part, add another property that tells the Web Part how many blog posts to show from the RSS feed. Use an int for the property and call it ItemCount:

public int ItemCount {
    get;
    set;
}

You’d like these properties to show up in the tool pane when editing the properties of the Web Part. Do this by adding two attributes to the two Web Part properties: WebBrowsable and Personalizable. For properties that have the WebBrowsable attribute, the Web Part framework in SharePoint will automatically generate a user interface and a valid input field, based on the type of the property. Because the first property is a string, SharePoint will generate a standard text input field. For the second property, another input field will be generated for inserting integers.

The Personalizable attribute tells the Web Part Manager that the value of this property is going to be stored in the SharePoint content database. SharePoint takes care of storing and retrieving these values. The parameter to the attribute, which you’ll look at in section 5.1.7, tells SharePoint how to store this attribute and in which mode it can be edited. You’d like these properties to be available in Shared mode, so specify that in the parameter. The RSS feed URL property should look like this after you add the attributes:

[WebBrowsable]
[Personalizable(PersonalizationScope.Shared)]
public string RssFeedUrl {
    get;
    set;
}

You need to do the same for the ItemCount property. If you now build and deploy this Web Part, you’ll be able to edit it. You can change and save the properties that appear under the Miscellaneous section in the tool pane, which uses the automatically generated web interface, as shown in figure 5.1.

Figure 5.1. Custom Web Part properties with the WebBrowsable attribute applied will be visible in the Miscellaneous section of the tool pane. The tool pane interface for the properties has been automatically generated by SharePoint based on the type of the properties.

You can now set the property values and click Apply to apply them and stay in Edit mode. If you click OK, the property pane will close and save your properties.

The Personalizable attribute can only be used on properties that match the following rules:

  • They must be public.
  • They must have both the set and get accessors.
  • They can’t have parameters.
  • They can’t be indexed.

Now open your site in SharePoint Designer and choose to edit the page in which you added the Web Part. Select the Web Part you just added and then, in the Ribbon, select View > Task Panes > Tag Properties. You’ll see a new pane (called Tag Properties) listing all the Web Part properties. Scroll down to the Misc section and you’ll see the custom properties, as shown in figure 5.2. SharePoint Designer will pick up all public properties and make them available for editing using the Tag Properties pane. You can also manually edit a property as an attribute to the Web Part tag in Source Code mode.

Figure 5.2. The Tag Properties panel in SharePoint Designer can be used to change the values of a Web Part or a control. This figure shows the custom properties of the custom RSS Web Part in the Misc category.

If you remove the WebBrowsable attribute from one of the properties and redeploy your solution, you’ll see that it disappears from the tool pane in the web browser. The property will still be available from SharePoint Designer 2010. You can use this technique to hide certain properties from users who only access the Web Parts using the web interface. As a good practice, you should use the WebBrowsable attribute and set the parameter to false if you don’t want to show it in the web interface:

[WebBrowsable(false)]

This will make your code easier to understand because it shows that you’ve made a choice not to show it in the web interface (and not that you just forgot to add the attribute!).

5.1.2. Customizing the property

You can see in figure 5.1 that the properties are displayed using the name of the property, which typically isn’t user friendly. To fix this, there’s another attribute you can use: WebDisplayName allows you to specify a friendly name to display in the web interface. To further enhance the user experience, add the WebDescription attribute, which lets you enter a brief description of the property. Figure 5.3 shows how your Web Part should look like after you add a custom display name.

Figure 5.3. The automatically generated tool pane of the RSS Web Part uses the attributes of the custom properties to generate the input fields and their descriptions.

The WebDisplayName and WebDescription attributes are added with the WebBrowsable and Personalizable attributes on your properties. The final code should look like the code in listing 5.1.

Listing 5.1. The two configurable Web Part properties for the RSS Web Part
[WebBrowsable]
[Personalizable(PersonalizationScope.Shared)]
[WebDisplayName("RSS feed Url")]
[WebDescription("Enter the Url to the RSS feed")]
public string RssFeedUrl {
    get;
    set;
}
[WebBrowsable]
[Personalizable(PersonalizationScope.Shared)]
[WebDisplayName("Items to show")]
[WebDescription("The number of RSS feed items to show")]
public int ItemCount {
    get;
    set;
}

To view the changes, click Build > Run to compile, deploy, and start the debugger. Once Internet Explorer has started, you can add the Web Part to a page, select it, and edit it if so desired. Your tool pane should look like figure 5.3. Hover your cursor over any of the input fields to see the description. SharePoint Designer will still display the actual property name. Note that if you don’t specify the WebDescription attribute, the display name will be used as the description.

5.1.3. Custom categories

All properties that you make available for editing in the web interface are categorized under Miscellaneous by default. This category is the default used when you haven’t specified any category for the property. To specify a category on the Web Part, you add another attribute to the Web Part called SPWebCategoryName. This attribute takes as a parameter a string that’s the name of the category. To add SPWebCategoryName, you have to include the namespace Microsoft.SharePoint.WebPartPages in the source file by using the using statement. But when you compile your project, you’ll get an error telling you that there’s an ambiguous reference to the WebPart class. This is because the SharePoint Web Part implementation resides in that newly added namespace. To work around the issue, you should edit the using statement so it looks like this:

using SP = Microsoft.SharePoint.WebPartPages;

This creates an alias for that namespace and you can add the attribute to your properties as follows instead of writing out the full namespace:

[SP.SPWebCategoryName("Feed Properties")]

Deploy your solution and go back to the page where you added the Web Part and edit it. When the property pane is open, you can see that your properties are no longer in the Miscellaneous section but in the category called Feed Properties, as shown in figure 5.4. SharePoint Designer will still categorize these properties in the Misc section, as in figure 5.2.

Figure 5.4. Custom categories can be applied to properties using the SPWebCategoryName attribute.

You can also use the Category attribute defined in System.ComponentModel. This attribute is defined in the Microsoft .NET Framework and doesn’t require any references to the SharePoint assemblies. It’s used by the property tool pane in SharePoint Designer and also works in the SharePoint web interface. The SharePoint category attribute has precedence over the .NET Framework attribute in the web browser. If you don’t specify the .NET Framework Category attribute, the property will be shown in the Misc section in SharePoint Designer. Using both of these attributes at the same time allows you to have your property appear in different categories in the web interface and SharePoint Designer editor. Read more about attribute precedence in section 5.1.5.

 

Note

If you try to add custom properties to one of the default categories in SharePoint, your property will end up in the Miscellaneous section.

 

5.1.4. Default values on properties

At this point, you haven’t added a body to the Web Part so that it displays the configured feed, so let’s do that next. The content of the Web Part is generated in the CreateChildControls method, in which you add the code shown in listing 5.2. This code requires you to add a using statement to the System.Linq and System.Xml. Linq namespaces.

Listing 5.2. Building the interface of the RSS Web Part
protected override void CreateChildControls() {
    XElement feed = XElement.Load(this.RssFeedUrl);

    BulletedList bulletedList = new BulletedList();
    this.Controls.Add(bulletedList);
    bulletedList.DisplayMode = BulletedListDisplayMode.HyperLink;

    var items = feed.Descendants("item").Take(this.ItemCount);

    foreach (var item in items) {
        bulletedList.Items.Add(new ListItem() {
            Text = item.Descendants("title").First().Value,
            Value = item.Descendants("link").First().Value
        });
    }
    this.ChildControlsCreated = true;
}

The RSS feed is loaded using the XElement.Load method. The BulletedList control is used to show the feed items; its display mode is set to HyperLink so that the user can click each item in the list. Each feed item is then retrieved using LINQ to XML. Using the LINQ extension methods Descendants and Take the first ItemCount number of item elements are fetched from the feed. For each feed item, the method will create a new ListItem object. The method sets the object’s Text property to the feed item’s title and the Value property to its URL.

If you build and deploy this Web Part and add it to a page, you’ll get an ArgumentNullException. This is because RssFeedUrl is a string whose default value is null, and when the Web Part tries to load the feed, it uses the default value (null) of the string type. To fix this issue, you can define a different default value—by initializing it in the constructor, for example. But even if you add a new default value, you must take care of any exceptions in case the user makes an invalid entry.

The code in listing 5.2 has no error handling or default values, which will cause it to throw an exception. So let’s continue adding code to the listing.

Default Values in the Web Part Description File

The most common method of setting default values is to use the Web Parts control description file and set the default values using the property element. You do this in Visual Studio by opening the .webpart file, located under the Web Part node in the Solution Explorer. To add a default value to a property, you add a new property element with a name attribute and a value. For the RSS Web Part, add two property elements to the file, like this:

<property name="RssFeedUrl">
http://feeds2.feedburner.com/WictorWilen/</property>
<property name="ItemCount">5</property>

If you deploy the solution and add the Web Part to a page, you’ll see five blog posts from the specified feed. When you edit the Web Part, you can enter new values to override the default values. Any updates to the default values in the .webpart file won’t be reflected in Web Parts that you’ve already added to the page—they’re copied to the Web Part when you insert it to a page.

If you’d like to supply the user with two or more property configurations of your Web Part, you can add a new .webpart file to your solution. First, right-click the .webpart file in your Web Part item in the Solution Explorer and choose Copy. Then right-click the Web Part item and choose Paste. You have now cloned the .webpart file. Rename the new file to RssWebPart2.xml. When you copy the file or add a new .webpart file to the Web Part item, Visual Studio detects this and automatically updates the elements manifest for the item. If you open the elements.xml file, you should see that the Module element looks like this:

<Module Name="RssWebPart" List="113" Url="_catalogs/wp">
    <File Path="RssWebPart\RssWebPart.webpart"
          Url="RssWebPart.webpart"
          Type="GhostableInLibrary">
      <Property Name="Group" Value="Custom" />
    </File>
    <File Path="RssWebPart\RssWebPart2.webpart"
          Url="RssWebPart2.webpart"
          Type="GhostableInLibrary">
      <Property Name="Group" Value="Custom" />
  </File>
</Module>

Open the new .webpart file and change the values of the RssFeedUrl and ItemCount properties to http://feeds2.feedburner.com/SharePointWebPartsInAction and 5, respectively. You should also change the Title property so that you can easily see the difference between the two Web Parts. When you deploy this package to the Web Part Gallery, the site collection will be populated with two configurations of the same Web Part.

Hardcoded Default Values

Another approach for default values, which doesn’t exclude the former one, is to initialize the properties in your Web Part class—for example, in the constructor. You should always have initial values of your properties that are configured so that you get no exceptions or other failures in your Web Part.

Default values are most important for reference typed properties, such as strings, which have no default values. Those properties are initialized as null and can cause null reference exceptions. Value typed properties such as integers and enumerations always have a default value. It’s a good idea to set a default value for those as well so that the Web Part is configured even if it has no Web Parts control description file.

The Defaultvalue Attribute

The DefaultValue attribute won’t set any default values for your Web Part. This attribute is only used by visual designers or code generators to provide default values to member properties when inserting code (although it might be a good practice to use this attribute in case your Web Part is used in these scenarios). To set a default value on the ItemCount property, add this attribute line to the property:

[DefaultValue(10)]
Handling Missing Property Values

There are situations when you can’t have any default values or you’d like the user to configure the Web Part as soon as it’s added to the page. A good convention is to inform users that the Web Part needs configuration before they use it. SharePoint has a JavaScript function that displays the Web Part’s tool pane, which the user can open to edit the properties of the Web Part. You can add a link that invokes this JavaScript method to your Web Part interface if the Web Part isn’t configured. In the CreateChildControls method of your Web Part, add a check if the feed URL is null or an empty string. Then add the link with the JavaScript, as in listing 5.3. Insert this code before the code from listing 5.2 that you previously added.

Listing 5.3. Handling of missing or invalid values in a Web Part
if (String.IsNullOrEmpty(this.RssFeedUrl)) {
    HyperLink hl = new HyperLink();
    hl.NavigateUrl = String.Format(
        "javascript:ShowToolPane2Wrapper('Edit','129','{0}');",
        this.ID);
    hl.ID = String.Format("MsoFrameworkToolpartDefmsg_{0}", this.ID);
    hl.Text = "Click here to configure the Web Part";
    this.Controls.Add(hl);
}

This snippet checks if the feed URL is null. If it is, the code creates a link control with a SharePoint built-in JavaScript function that opens the tool pane. The JavaScript needs the id of the Web Part to open the correct tool pane. Figure 5.5 shows how the Web Part will look if it has no default values using the technique just described.

Figure 5.5. A nonnconfigured Web Part should show a user-friendly interface that helps the user configure it.

 

Note

The ShowToolPane2Wrapper JavaScript method is an internal and unsupported method in SharePoint that might be deprecated or changed in future versions.

 

5.1.5. Properties used in the Web Part class and defined in SharePoint

The SharePoint-based Web Part also uses attributes to customize how properties are used and their appearance. There’s some dissimilarity, though, in which attributes are used. The attributes WebDisplayName and WebDescription were introduced in the ASP.NET 2 Framework and Web Parts based on the SharePoint implementation used the FriendlyName and Description attributes, respectively. The FriendlyName attribute is defined in the Microsoft.SharePoint assembly in the Microsoft.SharePoint.WebPartPages namespace. The Description attribute is, just like the previously discussed Category attribute, defined in the .NET Framework.

Web Parts based on the SharePoint implementation can also use a special attribute called Resource for localization. The attribute takes three parameters containing resource identifiers for the name, category, and description. SharePoint then uses the LoadResource method in the SharePoint Web Part implementation to load the localized value. This attribute can’t be used on ASP.NET-based Web Parts.

SharePoint 2010 still supports all these variants for backward compatibility. You can even combine the different attributes, but then it’s important to know which attribute SharePoint prioritizes first. Table 5.1 shows in which order SharePoint uses the attributes.

Table 5.1. The order in which SharePoint selects the attributes on Web Part properties when displayed in the web interface

Order of usage

Display name

Description

Category

1 WebDisplayName WebDescription SPWebCategoryName
2 Resources Resources Resources
3 FriendlyName Description Category

Note that only Web Parts derived from the SharePoint Web Part implementation can use the Resources attribute. Also, don’t forget to use both the SPWebCategoryName and Category attributes to support categories in SharePoint Designer. As a best practice don’t use the Resources attribute in your Web Parts because it might be deprecated in future versions.

5.1.6. Properties in Visual Web Parts

If you’re building a Visual Web Part, you have to define the properties in the Web Part class, not the user control. SharePoint or SharePoint Designer won’t pick up or store properties that are defined in any underlying control. You need to include extra plumbing when working with Visual Web Parts and Web Part properties to ensure that the properties are copied or referenced to the user control (if it needs the properties). The values of the properties exist in the Web Part before the OnInit method, as you saw in chapter 4, so you can use the property value in the CreateChildControls method.

For a Visual Web Part that should print the value of a Web Part property in the user control, you need to copy the value from the Web Part to the user control. One way to accomplish this, is to add a public property on the user control that exposes the Text property of a Label control as follows:

public string LabelText {
    get {
        return label1.Text;
    }
    set {
        label1.Text = value;
    }
}

This property is set in the CreateChildControls of the Web Part when the user control is created. The following code also casts the control loaded by Page.LoadControl into the type of the user control:

protected override void CreateChildControls() {
    VisualWebPart1UserControl control = Page.LoadControl(_ascxPath)
        as VisualWebPart1UserControl;
    control.LabelText = this.Text;
    Controls.Add(control);
}

As you can see, you need to do more coding if you want to use the values of Web Part properties in the Visual Web Part user control.

5.1.7. Personalization

SharePoint has the power to use personalized pages and Web Parts. Personalization support allows users to configure the content and Web Parts, resulting in a dynamic and interesting interface. Personalization isn’t always a good thing, though; it demands maintenance and support and could possibly break Web Part applications. As a Web Part developer, you have the ability to control what parts can be customized and what parts can be personalized.

The Personalizable attribute controls whether a property can be edited by users and specifies whether its value needs to be stored (serialized into the content database). You used it in the previous sample to set the property to be editable by setting the personalization scope to Shared. When a property has the personalization scope set to Shared, it can only be changed by authors who are permitted to edit the page. You can also set the scope to User, which is the default mode. If the scope is set to User, then any user with personalization permissions can change the value of this property. User-scoped changes are only reflected on the page of the user who changed the property, not for any other user. When a user is editing a page or Web Part in Personal mode, only properties scoped to User can be changed. If a property has Shared scope, it can be edited in both Personal and Shared mode.

Web Parts based on the SharePoint implementation might have the WebPartStorage attribute instead of the Personalizable attribute. The WebPartStorage attribute is still valid for backward compatibility, but when you’re upgrading or building a new Web Part, use the Personalizable attribute. The Personalizable attribute will take precedence over the WebPartStorage attribute once the Web Part has been modified by the user.

5.1.8. Storing URLs in Web Part properties

Storing a URL in a Web Part is as simple as creating a property with the string or System.Uri type. The Visual Studio Code Analysis tools suggest using the Uri type, because it’s a part of the .NET design guidelines from Microsoft.

Storing URLs to SharePoint can be problematic if the Web Part is exported and then imported on another server. The address to the server might be different and that might potentially break your application. To resolve this issue, use the ManagedLink attribute. This attribute, which can be set on properties that have a Shared personalization scope, converts absolute addresses into server relative addresses, and vice versa. The attribute has two parameters:

  • The Fixup parameter indicates whether the value of the property should be corrected by removing redundant .. and . characters as well as canonicalizing the protocol and server address.
  • The ConvertServerLinksToRelative specifies whether the links should be converted from server links to relative links. Both these properties are set to true by default. The following property would convert http://server/SitePages/Home.aspx to /SitePages/Home.aspx:
    [WebBrowsable(true)]
    [Personalizable(PersonalizationScope.Shared)]
    [Microsoft.SharePoint.WebPartPages.ManagedLink]
    public Uri ManagedFixup {
    get;
    set;
    }

Even if an absolute URL is entered in the property, SharePoint will store and display the URL as a site-relative URL. If the ConvertServerLinksToRelative is set to false, the property will be stored as is but with a canonicalized server address. For instance, http://SERVER/default.aspx will be converted to http://server/default.aspx.

5.1.9. Static Web Parts

Static Web Parts—that is, Web Parts that don’t exist in a Web Part zone—can’t be customized or personalized. Their properties can be set programmatically but not in the user interface, and the values aren’t stored in SharePoint.

Say you’ve built your Web Part so that it’s in a nonconfigured state and provides the user with a link to open the tool pane for the Web Part. As shown in listing 5.3 earlier, the user will get an error when clicking the link for a static Web Part. The error is thrown because static Web Parts can’t be edited in the web interface. To prevent this configuration link from being rendered and thus avoiding the error, you could use the IsStatic property of the Web Part, as in listing 5.4. This property returns true for all Web Parts that are static.

Listing 5.4. Handling of invalid configuration in a static Web Part
if (String.IsNullOrEmpty(this.RssFeedUrl)) {
    if (this.IsStatic) {
        Label label = new Label();
        label.Text = "Not configured";
        this.Controls.Add(label);
    }
    else {
        // Code from listing 5.3
    }
}

When you need controls outside of the zone, Web Parts aren’t always the best solution. Instead, consider using standard web controls or other approaches such as delegate controls.

 

Tip

You can learn more about delegate controls at the Microsoft MSDN site: http://msdn.microsoft.com/library/ms463169.aspx

 

5.1.10. Web Part previews

When you click on a Web Part in the Web Part Gallery, the Web Part preview page (/_layouts/WPPrevw.aspx) appears with a preview of the Web Part. This page renders the Web Part as a static Web Part using the default properties from the Web Part control description file.

The preview is generated by the WebPartPreview control that loads the Web Part as a static Web Part. To create a custom preview of your Web Part, use the following code in the CreateChildControls method:

if ( this.Parent is WebPartPreview ||
    base.DesignMode ) {
        this.Controls.Add(new Literal() {
            Text = "Preview mode"
        });
    }

This snippet checks whether the parent control is a WebPartPreview control or whether it’s been generated in a designer such as SharePoint Designer or Visual Studio. If one of these conditions is true, the code builds the preview of the Web Part.

5.1.11. Error handling in Web Part properties

When working with Web Part properties, you’ll need to have some kind of error handling for the properties. For instance, you might have a string property that should be a valid URL or a decimal number. It’s a good practice to handle the validation of these properties directly in the property instead of in the user interface code. You have two options:

  • Use Editor Parts (explained in section 5.3)
  • Use the WebPartPageUserException

The WebPartPageUserException is a specific exception that’s handled by the Web Part infrastructure and shows a friendly error message in the user interface instead of the default SharePoint error message. Let’s say that you have a Web Part property that must contain a valid URL and that property is defined as a string. A correct way to implement this property is as follows:

private string m_url;
[WebBrowsable]
[Personalizable(PersonalizationScope.Shared)]
public string RssFeedUrl {
    get{
        return m_url;
    }
    set {
        if(!Uri.IsWellFormedUriString(value, UriKind.Absolute)) {
            throw new WebPartPageUserException("Not a well formed URL");
        }
        m_url = value;
    }
}

The setter method of this property uses the static method IsWellFormedUriString of the Uri class to verify that the URL is a valid absolute URI. If it’s not, a WebPartPageUserException is thrown with an error message that informs the user of the error; otherwise, the value is accepted and stored in the private variable.

5.2. Common Web Part properties

All Web Parts are derived from the WebPart class defined in the System.Web.UI.WebControls.WebParts namespace and have a set of common properties. These properties are divided into three main groups:

  • Appearance
  • Layout
  • Advanced

You can use these properties when you’re developing your Web Parts. You can also override them if you need to make any changes. You’re going to look at the three groups and focus on some of the important default properties. Each of these groups is handled by special SharePoint controls, which can’t be modified.

5.2.1. Appearance properties

The Appearance group is focused on the general layout of the Web Part with properties such as Title, Width, and Height. Title is displayed in the header of the Web Part. Width and Height are by default set to adapt to the Web Part zones but can be set to fixed sizes.

This group also contains chrome configurations. The chrome is everything surrounding the Web Part, such as the title bar and border. You can specify the type and state of the chrome. Chrome State can be set to either Minimized or Normal and can be changed from the Web Part Options menu as well. Chrome Type specifies whether the border and/or title of the Web Part are shown. Table 5.2 lists all available properties in the Appearance group.

Table 5.2. Appearance Web Part properties

Property name

Description

Title Specifies the title of the Web Part.
Height Specifies the height of the Web Part. The default is Adjustable Height.
Width Specifies the width of the Web Part. The default is Adjustable Width.
Chrome State Specifies the initial state of the Web Part. Possible values are Normal and Minimized.
Chrome Type Specifies the style of the chrome. Possible values are Default, None, Title and Border, Title Only and Border Only.

5.2.2. Layout properties

The Layout group contains configuration options related to how the Web Part is placed and how it acts within the Web Part zone. Using these properties, you can change the zone of the Web Part and its position within the zone. These configurations are seldom used because the rich web interface has support for dragging and dropping the Web Parts. Most of these properties are only valid for Shared mode.

One interesting feature in the Layout group is the ability to hide a Web Part. A hidden Web Part isn’t displayed in the browser but can still execute actions and connect with other Web Parts. In comparison, a closed Web Part doesn’t act at all on a page. All available properties in the Layout group are listed in table 5.3.

Table 5.3. Layout Web Part properties

Property name

Description

Hidden Specifies whether the Web Part is visible. Can’t be used in wiki content zones.
Direction Specifies the direction of text in the Web Part.
Zone Specifies in which zone the Web Part is located.
Zone index Specifies the index of the Web Part in the zone. Can’t be changed in wiki content zones.

5.2.3. Advanced properties

The Advanced group contains specific Web Part configuration options. You can use these properties to prohibit users from closing or hiding Web Parts and control what properties can be exported. This group is only available in Shared mode.

You can control which properties can be exported by the users, when using the Export command from the Web Parts menu. The Export Mode property in the Advanced group controls what’s exported; this property has the following possible values:

  • Do Not Allow— The Web Part can’t be exported and the Export command is hidden in the user interface.
  • Export All Data— All properties are exported.
  • Non-Sensitive Data Only— Only nonsensitive properties are exported.

When you have properties that you don’t want users to export, such as a password or other sensitive information, you can set the property to Sensitive. To do so, use the Personalizable attribute and set the IsSensitive parameter of the attribute to true. The default value is false. The following attribute will make the property a userscoped property, and the second parameter sets the IsSensitive property of the attribute to true:

[Personalizable(PersonalizationScope.User, true)]

A property with a Personalizable attribute configured like this won’t be exported if the ExportMode property of the Web Part is set to NonSensitiveData.

The export mode can be changed using the advanced properties, but if you want to prevent administrators or developers from changing this value, you can override the ExportMode property and hardcode the NonSensitiveData value. The following snippet shows how:

public override WebPartExportMode ExportMode {
    get {
        return WebPartExportMode.NonSensitiveData;
    }
    set {
    }
}

The advanced properties contain other configuration properties that you can set, as shown in table 5.4.

Table 5.4. Advanced Web Part properties

Property name

Description

Allow Minimize Determines whether the Web Part can be minimized.
Allow Close Allows users to close the Web Part. Set to false to prevent users from closing the Web Part.
Allow Hide Indicates whether the Web Part can be hidden.
Allow Zone Change Indicates whether the zone of the Web Part can be changed.
Allow Connect Controls whether Web Part connections can be used for this Web Part.
Allow Editing in Personal View Controls whether the user can change this Web Part in the personal view.
Help URL Specifies the URL for this Web Part’s help page. The help link appears in the Web Parts menu when you enter it.
Help Mode Determines how the browser opens the Help URL.
Catalog Icon Image URL Specifies the icon shown in the Web Part Gallery.
Title Icon Image URL Specifies the icon shown in the Web Part title.
Import Error Message The error message that is displayed when an error occurs while importing this Web Part.

In SharePoint Server Standard or Enterprise edition, the advanced properties have an additional property called Target Audiences. Audience is a feature of SharePoint Server that allows you to have dynamic groups based on a set of rules. This property allows you to select an Audience for this Web Part; no other Audiences will see the Web Part. This property is added by a runtime filter in the Server edition of SharePoint. You can create your own runtime filter if you need a custom filter or if you’d like to add similar functionality to SharePoint Foundation. You’ll learn how in section 5.5.

5.3. Custom Editor Parts

So far, you’ve covered how you can use a declarative programming approach to make your Web Parts customizable. Using simple property types and specific attributes, SharePoint takes care of creating a user interface for customizing the Web Parts. You now know how to create a property that can be edited by users and administrators. But if you’d like to have validation on your input fields or have dependencies between them, this approach doesn’t work. To be able to add these functionalities to the properties, you have to create a custom Editor Part. You’ve already seen a few Editor Parts in the previous section:

  • The tool pane consists of Editor Parts.
  • The Layout property, LayoutEditorPart, is an Editor Part.
  • The properties that you marked as WebBrowsable appear in another Editor Part, the CustomPropertyToolPart.

This section shows you how to create custom Editor Parts with validation and properties. You’ll continue working with our RSS Web Part and add a custom Editor Part to it. To learn how to validate your input and create dependencies between the properties, you’ll extend the functionality of the RSS Web Part.

 

Tip

With the .NET Reflector tool, from Red Gate Software Ltd., you can inspect and browse the contents of any assembly. To understand how the built-in Editor Parts work, take a look at CustomPropertyToolPart, LayoutEditorPart, and other Editor Parts in the Microsoft.SharePoint assembly. You can download the Reflector tool for free at http://www.red-gate.com/products/reflector/.

 

5.3.1. Create a custom Editor Part

In our RSS Web Part, two properties are simple input values. They work as they should without validation, but the user could write anything in those text boxes. The feed URL should be validated so that it’s a correct URL and the number of items to show should always be an integer equal to or larger than 1.

To create a custom Editor Part, the first thing you do is add a new class to your Web Part item. Right-click the Web Part item and select Add > Class. For this example, enter the name RssWebPartEditorPart as the name of the class.

To create the Editor Part, make sure that your class inherits from the base EditorPart class, defined in the System.Web.UI.WebControls.WebParts namespace. To inherit from the EditorPart, you edit the class definition like this:

class RssWebPartEditorPart: EditorPart

The user interface of the Editor Part is built by creating the input fields in the CreateChildControls method, as shown in listing 5.5.

Listing 5.5. Creation of the controls in the Editor Part
protected TextBox m_feedUrl;
protected TextBox m_itemCount;

protected override void CreateChildControls() {
    m_feedUrl = new TextBox(){ID = "feedUrl"};
    m_itemCount = new TextBox() {ID = "itemCount"};

    this.Controls.Add( new Label() {
        Text = "Feed Url:<br/>",
        AssociatedControlID = m_feedUrl.ID
    });
    this.Controls.Add(m_feedUrl);

    this.Controls.Add( new Label() {
        Text = "<br/>Items to show:<br/>",
        AssociatedControlID = m_itemCount.ID
    });
    this.Controls.Add(m_itemCount);

    this.Title = "Feed Properties";

    base.CreateChildControls();
    this.ChildControlsCreated = true;
}

The input fields are defined as protected and created in CreateChildControls. The controls are then added to the child controls collection. The Title property is set to the title of the Editor Part; this is especially important if you have several Editor Parts. Notice how the Label controls are added and associated with the input form controls.

To read information from the Web Part that’s being edited, the EditorPart has a method called SyncChanges. You have to override this method and read the property values from the Web Part and then set the values of the local Editor Part controls, as in listing 5.6.

Listing 5.6. The SyncChanges method
public override void SyncChanges() {
    EnsureChildControls();
    RssWebPart webPart = this.WebPartToEdit as RssWebPart;
    if (webPart != null) {
        m_feedUrl.Text= webPart.RssFeedUrl;
        m_itemCount.Text = webPart.ItemCount.ToString();
    }
}

The SyncChanges method retrieves the Web Part that it’s associated with and then assigns the values from the Web Part to the local controls.

The property values are written back to the Web Part when you click OK or Apply in the tool pane. When saved, the EditorPart invokes a method called ApplyChanges that you need to implement so that it copies the values from the Editor Part to the Web Part. The Listing 5.7 shows you how this method is implemented.

Listing 5.7. The ApplyChanges method
public override bool ApplyChanges() {
    EnsureChildControls();
    RssWebPart webPart = this.WebPartToEdit as RssWebPart;
    if (webPart != null) {
        webPart.RssFeedUrl = m_feedUrl.Text;
        webPart.ItemCount = Int32.Parse(m_itemCount.Text);
    }
    return true;
}

Just as in the SyncChanges method, you have to retrieve the associated Web Part and then set the properties of the Web Part to the values of the local controls. Finally, you have to return true to indicate that the operation was successful. If the ApplyChanges method returns false, then the SyncChanges method won’t be invoked, because you instruct SharePoint that an error has occurred.

This is all that’s needed to create the custom Editor Part. The next step is to associate the Editor Part with the Web Part, instead of the automatically generated controls.

5.3.2. Add the custom Editor Part to the Web Part

The Editor Part you created is currently only a class defined in the solution and has no connection to the Web Part. To make the Web Part use this Editor Part instead of using the interface generated from the properties marked with the attributes discussed earlier, you need to make the Web Part implement the IWebEditable interface. Edit the class definition for your Web Part so that it looks like this:

public class RssWebPart : WebPart, IWebEditable

This interface exposes one property and one method and tells SharePoint and ASP.NET that this control exposes custom Editor Parts. The property is called WebBrowsableObject and should return a reference to the Web Part or control that’s going to be edited by the Editor Part controls. The method called CreateEditorParts returns a collection of EditorPart controls. A Web Part can have several Editor Parts, which can be useful if you want to group or organize your configuration for a better user experience.

 

Tip

To see how a class or interface is defined, you can use Visual Studio to show you the definition by selecting the class or interface. Then press the F12 key or right-click and select Go To Definition. If you do this on IWebEditable in your Web Part class definition, you’ll see the definition of the property and method.

 

To implement the property and method, right-click on IWebEditable in your class definition and select Implement Interface > Implement Interface Explicitly. Visual Studio will then create the implementation of the interface at the end of your Web Part source code file.

Listing 5.8 shows how the WebBrowsableObject property returns the Web Part object. As you can see, the CreateEditorParts creates a collection of Editor Parts and returns the collection.

Listing 5.8. Implementation of the IWebEditable interface

In the CreateEditorParts method, you have to create a List<EditorPart> object to add your custom Editor Parts. You have to specify a unique identifier for each Editor Part . A good convention is to use the id of the Web Part and append _editorPart. The EditorPart object is added to the list and finally returned from the method. The WebBrowsableObject is used by the Editor Part to get a reference to the Web Part that’s being edited. This method needs to return the object itself.

The ASP.NET Web Part definition already exposes this interface. Instead of explicitly implementing this interface, you could just override the CreateEditorParts method. I’ll show you the explicit method here so you’ll see how the interface works.

Now your Editor Part is associated with the Web Part. Build and deploy the solution to SharePoint so that you can add the Web Part to a page. When the Web Part appears on the page, you can edit its properties. Your custom Editor Part will appear on the right and look like figure 5.6.

Figure 5.6. Adding an Editor Part to the Web Part allows the developer to control the logic in the tool pane. The properties may appear both in a custom Editor Part and in the default interface if the WebBrowsable attribute is present on the property declaration.

In figure 5.6, you can also see that the automatically generated interface is still available. To remove the properties from that category, you need to modify the WebBrowsable attribute so that it has the value set to false or remove the attribute from the property. Once that’s done, you can once again deploy your project and test the Web Part.

5.3.3. Validating properties

You can further enhance your custom Editor Part with validation. Validation ensures that users enter a correct value to avoid error messages in the Web Parts. If you have a percentage input, you could validate the property so that its value is between 0 and 100. Not only does the validation avoid unnecessary error messages, it also improves the overall user experience.

You should use validation when it’s applicable, but you shouldn’t rely on it. The validation is only used when users are editing the Web Part in the web interface using the custom Editor Part. If they’re editing the Web Part in code or manually editing it with SharePoint Designer, the custom Editor Part and its validation won’t be used. As usual, you always need good error handling in your Web Part. Chapter 8 explains how to implement good exception handling and error messages.

In the RSS Web Part, the RSS feed URL should be validated so that it’s a valid URL. You accomplish this by adding a regular expression validation control. Add the control in the CreateChildControls method, directly after adding the feed URL text box:

RegularExpressionValidator regValidator = new RegularExpressionValidator {
    ControlToValidate = m_feedUrl.ID,
    ValidationExpression = @ "([^:]+)://(([^:@]+)(:([^@]+))?@)? ([^:/?#]+)(:([d]+))?([^?#]+)?(\?([^#]+))?(#(.*))?",
    ErrorMessage = "Please provide a valid Url"
};
this.Controls.Add(regValidator);

Because you also created a text box for entering the number of items to fetch, you have to make sure that the user is entering an integer. For that validation, add a range validator:

RangeValidator rangeValidator = new RangeValidator {
    ControlToValidate = m_itemCount.ID,
    MinimumValue = "1",
    MaximumValue = "100",
    Type = ValidationDataType.Integer,
    ErrorMessage = "Enter a value between 1 and 100"
};
this.Controls.Add(rangeValidator);

Build and deploy the Web Part once again and try different settings for the feed URL and the number of items to verify that your validation and Web Part works. If you enter invalid values, you’ll see the error message shown in figure 5.7.

Figure 5.7. Validators can be used in custom Editor Parts to validate the user input.

One problem with validators is that if you enter invalid data and try to click the Cancel button in the tool pane, nothing happens. The Cancel button invokes the validators and therefore can’t perform its cancel action because they’re invalid. You can work around this by turning off the validation of the Cancel button. Cast the Zone property of the Editor Part to a ToolPane object and then change the behavior of the Cancel button like this:

ToolPane pane = this.Zone as ToolPane;
if (pane != null) {
    pane.Cancel.CausesValidation = false;
}

This code snippet is added to the CreateChildControls method and casts the Zone property of the Editor Part to a ToolPane object. This ToolPane object corresponds to the tool pane and exposes the Cancel button so you can turn off its validation.

5.3.4. Property dependencies

If you’re creating advanced Editor Parts, you might need to create one that adapts to the selections that the user is making. You can use standard ASP.NET postbacks to make the Editor Part react to the user’s actions.

Suppose you want to add caching to the RSS Web Part. Wouldn’t it be great to have the caching and cache time configurable? Just add two properties to the Web Part: one indicating if caching is on and another specifying the time to cache the data:

[Personalizable(PersonalizationScope.Shared)]
public bool CacheData {
    get;
    set;
}
[Personalizable(PersonalizationScope.Shared)]
public int CacheTime {
    get;
    set;
}

Because you’re going to use these properties in your Editor Part, you only need to specify the Personalizable attribute so that the values are stored correctly. To be able to edit these values, add a Check box and another TextBox to your Editor Part:

protected Check box m_cache;
protected TextBox m_cacheTime;

Create these controls in the CreateChildControls method after the other controls. Listing 5.9 shows how the controls are created and added to the controls collection.

Listing 5.9. Event handlers and postbacks added to the Editor Part controls
this.Controls.Add(new Label(){
    Text = "<br/>Cache data:<br/>",
    AssociatedControlID = "cache"});
m_cache = new Check box() { ID="cache" };
m_cache.CheckedChanged += new EventHandler(m_cache_CheckedChanged);
m_cache.AutoPostBack = true;
this.Controls.Add(m_cache);

this.Controls.Add(new Label(){
    Text = "<br/>Cache time:<br/>",
    AssociatedControlID="cacheTime"});
m_cacheTime = new TextBox() { ID = "cacheTime" };
this.Controls.Add(m_cacheTime);

First create the check box and add an event handler when the checked status changes. To make the Editor Part reload immediately, set the AutoPostBack property of the check box to true, which will trigger the event handler. The event handler is a simple method that enables the cache time text box, if it’s checked, and disables the text box otherwise:

void m_cache_CheckedChanged(object sender, EventArgs e) {
    m_cacheTime.Enabled = m_cache.Checked;
}

Now, when you change the checked status of the cache check box, the cache time text box will be disabled or enabled. Modify the ApplyChanges method so that you copy the values from the controls to the properties of the Web Part:

webPart.CacheData = m_cache.Checked;
webPart.CacheTime = int.Parse(m_cacheTime.Text);

In SyncChanges, copy the properties from the controls of the Editor Part to the Web Part. You also need to enable or disable the text box depending on the cache settings:

m_cache.Checked = webPart.CacheData;
m_cacheTime.Enabled = webPart.CacheData;
m_cacheTime.Text = webPart.CacheTime.ToString();

That’s all you have to do to get a more dynamic Editor Part and to make it more intuitive for the end user. Now deploy the Web Part and test different settings (see figure 5.8). The actual caching isn’t yet implemented—you’ll take a closer look at caching in chapter 9.

Figure 5.8. Custom Editor Parts can be used when properties are dependent on other properties. The Cache Time text box shown here is disabled because the check box isn’t selected.

5.3.5. Visual appearance of the Editor Part interface

So far, you’ve just added controls to the Editor Part without considering the visual appearance. This aspect is just as important as the functionality, and it makes the Web Part look more professional. The best practice is to use a layout that looks as much as possible like the out-of-the-box Editor Parts, such as the Editor Parts used for the common properties. For example, the automatically generated text boxes have a corresponding button that you can click to edit the text in a dialog box. These classes exist in the SharePoint internal API and aren’t accessible for use in custom code.

 

Note

The Microsoft.SharePoint assembly contains a class called BuilderButton that creates the button used to display a dialog box for entering text in an Editor Part. This class is marked as internal and can’t be used by custom code. With the use of .NET Reflector, you can inspect that class and understand how it works to build a similar experience for your Editor Parts.

 

All styling of the Editor Parts have to be done manually. The best way to enhance your Editor Parts is to look at the out-of-the-box Web Parts and mimic their visuals. Most of the styling is done with a set of CSS classes. Table 5.5 contains some of the classes used to build the tool pane interface.

Table 5.5. CSS classes used when creating custom Editor Parts

CSS Class Names

Description

UserSectionTitle Used on the titles of the categories.
UserGeneric Used for the main DIV element or panel control surrounding all other controls.
UserSectionHead Used on an inner panel control for headings.
UserSectionBody Used on an inner panel control containing input controls.
UserControlGroup Used inside a UserSectionBody panel when multiple controls are used.
UserInput Used on text box input controls.
UserSelect Used on drop-downs.
UserDottedLine Used on panel controls to create a separator.

Apart from these classes, you should specify the width of text boxes as 176 pixels. For numeric input, set the text-align CSS attribute to right and the number of columns to 10. These values are the ones used internally by SharePoint when creating custom Editor Parts.

If you rearrange the Editor Part and apply the CSS classes in table 5.5, the Editor Part will look like figure 5.9. Compare that interface to the one in figure 5.8 and you’ll see the improvements.

Figure 5.9. Building a professional-looking interface for custom Editor Parts enhances the user experience.

Creating this interface takes a lot of code. Listing 5.10 shows how the figure 5.9 interface is built.

Listing 5.10. Styling of the controls in the Editor Part

The first control you need to create is UserGeneric , which will be the control that you add to the Editor Part control tree. Next, add a LiteralControl with a description of the Editor Part to be followed by the input for the properties. Each group of properties has a head with a title and then a body , and a group . The group contains the input fields. The text boxes are configured to use a CSS class and a width. Just before the validator, a line break is inserted and the validator is changed to a dynamic validator so that it doesn’t occupy space while not visible. This procedure is repeated for each property group. For the numeric input fields, you have to modify the text alignment and the number of columns. The two major groups of properties are separated by a dotted line .

5.4. Advanced properties

Until now, you’ve looked at simple properties such as strings, Booleans, and numbers. What if you need to store something more complex, such as a coordinate or an array of items? The easiest way is to build a custom Editor Part for the property. This complex property could be converted into a complex representation or into several properties at the backend. The downside of this approach is that the developers working with this Web Part in code or in SharePoint Designer will find it difficult to interpret its usage without proper documentation. It’ll likely result in more errors and troubleshooting. A better approach is to leverage .NET features such as type converters. Doing so will allow you to use the out-of-the-box interfaces in SharePoint, Visual Studio, and SharePoint Designer to convert a complex object into a string representation.

This section shows you how to create a custom Web Part property for a coordinate property that can be edited in both the web interface and SharePoint Designer and then be stored and retrieved.

5.4.1. Define the property

In this example you’ll create a new Empty SharePoint project called WebPartsInAction.Ch5.CoordinateWebPart, choose to use a farm solution, and add a new Web Part project item called CoordinateWebPart. Start with adding a new class to the project called Coordinate and define the coordinate class, as in listing 5.11.

Listing 5.11. The definition of the Coordinate class
[Serializable]
public class Coordinate {
    public float Latitude;
    public float Longitude;
    public override string ToString() {
        return this.Latitude.ToString(CultureInfo.CurrentCulture) +
            ":" +
            this.Longitude.ToString(CultureInfo.CurrentCulture);
    }
}

As you can see, this class has been marked as Serializable so that it can be serialized. It’s a simple class with Latitude and Longitude fields and a ToString method that creates a visual representation of the class. Also, notice that you’re using the CultureInfo.CurrentCulture (from the System.Globalization namespace) as a parameter to the ToString method to make sure that the float values are correctly displayed in all cultures.

In the Web Part, add a property as shown in listing 5.12 where CreateChildControls writes the value of the coordinate in a Label control.

Listing 5.12. The Coordinate Web Part definition
public CoordinateWebPart() {
    this.CurrentPosition = new Coordinate();
}

protected override void CreateChildControls() {
    this.Controls.Add( new Label() {
        Text = CurrentPosition.ToString()
    });
    base.CreateChildControls();
}
[WebBrowsable]

[Personalizable]
public Coordinate CurrentPosition {
    get;
    set;
}

In the constructor of the Web Part, initialize the local Coordinate variable to avoid any null reference exceptions. The CreateChildControls that’s overridden adds a new Label control to the child controls collection that prints the value of the coordinate. The Web Part also contains a property called CurrentPosition that’s personalizable.

5.4.2. Problems with complex types

At this point, deploy the Web Part into SharePoint and add it to a page. You’ll see that it nicely prints the value of the Coordinate property. Then select it and notice that the property isn’t available for editing. SharePoint doesn’t understand how to create interfaces for properties of complex types.

If you open the page in SharePoint Designer, you’ll see an error telling you that SharePoint can’t convert a Coordinate object from its string representation. Switch to the code view and locate the Web Part tag, and notice that it has a CurrentPosition attribute with a correct value. The value of the attribute is generated from the ToString implementation of the Coordinate class. It should look like this:

<WpNs0:CoordinateWebPart
    runat="server"
    CurrentPosition="0:0"
    ...>
</WpNs0:CoordinateWebPart>

Using SharePoint Designer, change the value of the CurrentPosition attribute to 1:1 and save the page. Then open the page in the web browser. You’ll see a similar error as before stating that SharePoint can’t create the Coordinate object from its string representation. The .NET Framework doesn’t know how to convert the string representation of the Coordinate class into a Coordinate object. Note that if you added the Web Part to a wiki page, you won’t see the error. You have to go to the Web Part maintenance page (by adding ?contents=1 to the URL) to see that it fails.

You’re facing two issues with a property like this:

  • Creating a Coordinate object from its string representation
  • Creating an interface for editing the property

The easiest way to work around these issues is to create two properties for Coordinate: a Latitude and a Longitude property. Doing so would solve both problems, but that’s not the best approach. Instead, you can leverage some of the .NET Framework’s features.

5.4.3. Create a type converter

The .NET Framework has classes and methods that allow you to convert types from one to another. The ExpandableObjectConverter class, defined in System.ComponentModel, is a base class that converts an expandable object to another representation. In this scenario, you’ll use this class to create an automatic conversion from the Coordinate class to a string representation, and vice versa.

To create the converter, start by adding a new class to the project; name it CoordinateConverter. It’s common to name converters with the object name and append Converter to the class name. Add using statements so that you have access to the converter and globalization namespaces:

using System.ComponentModel;
using System.Globalization;

Then make the class inherit from ExpandableObjectConverter and implement it, as shown in listing 5.13.

Listing 5.13. The expandable Converter class for the Coordinate object

In this Converter class, you need to implement four methods. The CanConvertTo method checks if this converter can convert to a Coordinate object. If this method returns true, then the .NET Framework needs the ConvertTo method to convert the object. This method returns the string representation of the Coordinate object if the destination type is a string and the value is a Coordinate. The third method, CanConvertFrom, is used to check if the converter can convert the object of the specified type to the type of the converter. This method returns true if the source type is a string. If it returns true, the conversion process needs the ConvertFrom method to perform the conversion. The method takes the supplied string value and splits it, and then creates a Coordinate object and sets the values. Finally, it returns the Coordinate object.

To let SharePoint and other applications using the Coordinate class know which converter to use, add the TypeConverter attribute to the Coordinate class. This attribute specifies the type of converter to use for the object at runtime. The class declaration of the Coordinate class should look like this:

[Serializable]
[TypeConverter(typeof(CoordinateConverter))]
public class Coordinate

When you’re done, deploy your Web Part and return to the page where you added the Web Part. If you select the Web Part, you’ll see that under the Miscellaneous section the property is available for editing. If you enter a new Coordinate and save the Web Part, the value is shown in the Web Part and stored.

If you choose to enter an invalid coordinate, you’ll get the error message shown in figure 5.10.

Figure 5.10. Complex properties implemented with .NET type converters are automatically validated in the Editor Parts.

When you’ve entered a valid coordinate, open the page in SharePoint Designer. Select the Web Part and choose View Tag Properties. Then scroll down to the Misc section and watch the CurrentPosition property. Just as in the web interface, you can edit the property and if you enter an invalid value you’ll get an error message (see figure 5.11).

Figure 5.11. SharePoint Designer doesn’t allow invalid values for complex properties that have a custom type converter.

This property can be further enhanced by adding an Editor Part in which you have input fields for the Coordinate, Latitude, and Longitude, to avoid error messages. The combination of the converter and an Editor Part makes the editing of your Web Part easy and understandable in both the web interface and using SharePoint Designer.

5.5. Runtime filters

In section 5.2, you looked at the standard Web Part properties and saw that the SharePoint Server has an extra property for targeting the Web Part to an audience. For instance, you might want to target one Web Part only to the Sales team and another one to the Support team. In this approach, the same page can be used for multiple audiences without cluttering the user interface with Web Parts that the current user doesn’t need. It’s a powerful feature that I miss in SharePoint Foundation because I often need to target Web Parts to specific groups or users. But the good news is that you can create your own runtime filter and create similar functionality yourself. This is something that’s not very well known or documented, so I’m going to show you how to create this custom filter. You’ll do this exercise as an end-to-end sample so that you can take the code and use it as is in your solutions.

The runtime filter in SharePoint is a specific class derived from the IRuntimeFilter2 interface. This class is registered in the web.config file and is called for every Web Part when rendering a page. A specific method in the class tells SharePoint whether it should be shown to the user. In this section you’ll create a custom runtime filter that can be used to target the Web Part using the site collection groups. This will give you the option to target Web Parts to specific groups even in SharePoint Foundation.

5.5.1. The AuthorizationFilter property

The WebPart class definition contains a string property called AuthorizationFilter. This property is used to store values of the runtime filter that applies to that specific Web Part. The AuthorizationFilter property isn’t directly editable from the web interface; you can only edit it using a runtime filter. The value of the property depends on the implementation of the runtime filter. This property is passed, by the WebPartManager object, to the runtime filter during page rendering to check whether the property has a valid value for the filter. You’ll use the name of the groups separated by commas as a string representation of the filter.

5.5.2. Create a runtime filter

To create a filter, you must create a project that contains a class that derives from the IRuntimeFilter2. This project must be a farm solution because it has to be registered in the global assembly cache (GAC). The first thing you need to do is add a new class item to your project; call it SiteGroupsRuntimeFilter. Make the class public so that it’s accessible from other assemblies.

This newly added class has to implement the IRuntimeFilter2 interface (from the Microsoft.SharePoint.WebPartPages namespace). It should look like this:

public class SiteGroupsRuntimeFilter: IRuntimeFilter2

To create the interface methods, you right-click on IRuntimeFilter2 and select Implement Interface > Implement Interface from the context menu. This will create the skeleton for the five methods that you need to implement. In this example, you’re implementing the IRuntimeFilter2, which is the predecessor of the now deprecated IRuntimeFilter interface (used in SharePoint 2003).

The Isfilteractive Method

The first method you’ll implement is IsFilterActive, which must return a Boolean value indicating whether this filter should be active. Make this method return the value true all the time so that it’s always visible:

public bool IsFilterActive() {
    return true;
}

You can use this method to conditionally use your filter on a specific site or site collection. The SharePoint Server Target Audiences implementation of the filter uses this method to enable the targeting only if the User Profile Service is enabled and online. For instance, you can make this method depend on a configurable setting so that administrators can turn the filter on or off.

The Initializestrings Method

The InitializeStrings method returns an array of strings, of which you only need to specify one: the title of your runtime filter property. The reason that it returns an array of strings is that SharePoint 2003 used four different strings to create the user interface. The method also takes a parameter indicating the culture used so that the returned strings can be localized. Your implementation of the method should look like this:

public ArrayList InitializeStrings(CultureInfo cultureInfo) {
    return new ArrayList(new string[] { "Target Groups" });
}

Note that you need to add using statements to System.Collections and System. Globalization.

The Validateisincludedfilter Method

To make sure that you’re storing correct values in the Web Part’s AuthorizationFilter property, the ValidateIsIncludedFilter is used. It takes a string as input and you add logic to validate that the string is correct and returns a correct string. The method is used after the user has inserted the values but before they’re saved to SharePoint. Normally this method just returns the original value like this:

public string ValidateIsIncludedFilter(string persistedString) {
    return persistedString;
}
The Checkruntimerender Method

The method used to check the AuthorizationFilter property is called CheckRuntimeRender. This method takes the value of the filter and contains your logic to determine if the Web Part will be shown. Listing 5.14 shows how it’s implemented to check whether the user is a member of any of the groups in the filter.

Listing 5.14. The CheckRuntimeRender method
public bool CheckRuntimeRender(string IsIncludedFilter) {
    SPContext context = SPContext.Current;
    if (String.IsNullOrEmpty(IsIncludedFilter)) {
        return true;
    }

    string[] filterArray = IsIncludedFilter.Split(new char[] {','});
    foreach(SPGroup group in context.Site.RootWeb.SiteGroups) {
        if (filterArray.Contains(group.Name) &&
            group.ContainsCurrentUser) {
            return true;
        }
    }
    return false;
}

First this method checks if the filter is empty, and if there’s no value, it returns true. Then it splits the incoming filter value on comma characters and iterates over the groups in the site collection. The method checks if the group exists in the filter and that the user belongs to that group. If that’s the case, it returns true; otherwise, it returns false.

The Gettoolpanecontrol Method

The last method that you need to implement is GetToolPaneControl. This method returns a web control that’s serves as the user interface when editing the property. This method needs to instantiate a WebControl that has the IToolPaneControl implemented. In the next section you’ll create a control called SiteGroupsFilterControl and the code for this method should look like this:

public IToolPaneControl GetToolPaneControl() {
    return new SiteGroupsFilterControl() { ID="SiteGroupsFilterControl" };
}

This method creates the control and assigns it an id. Note that you haven’t yet created the SiteGroupsFilterControl, so Visual Studio will show you an error if you try to compile it.

5.5.3. Create the user filter control

The control that you need for the web interface is a class inherited from the WebControl class. and you create it by adding a new class item to the project. Name it SiteGroupsFilterControl. You could also add a new ASP.NET Server Control item instead, but you’ll use the empty class to show all steps. First you have to make it inherit from the WebControl class and implement the IToolPaneControl interface that’s needed by the GetToolPaneControl method in the runtime filter. Add these using statements:

using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebPartPages;

Then modify your class definition as follows:

public class SiteGroupsFilterControl: WebControl, IToolPaneControl

You have to implement the IToolPaneControl interface on this control. It only contains one simple property, called Text, that’s used to get the value from the control. Implement it using an automatic property like this:

public string Text {
    get;
    set;
}

To avoid any null reference exception to this property, initialize the property to an empty string in the constructor. Create a constructor like the following and initialize the property:

public SiteGroupsFilterControl() {
    this.Text = String.Empty;
}

Create the user interface using any method that you normally use to work with web controls. In this sample you’ll build the user interface using a list box that you create in the CreateChildControls method, as in listing 5.15.

Listing 5.15. The runtime filter control for selecting the target groups
protected ListBox listBox;

protected override void CreateChildControls() {
    listBox = new ListBox();
    listBox.ID = "Groups";
    listBox.SelectionMode = ListSelectionMode.Multiple;

    SPContext context = SPContext.Current;

    var currentSelection = Text.Split(new char[] { ',' });
    foreach (SPGroup group in context.Site.RootWeb.SiteGroups) {
        listBox.Items.Add(
            new ListItem() {
            Text = group.Name,
            Selected = currentSelection.Any( g => g == group.Name )});
    }
    this.Controls.Add(listBox);
    base.CreateChildControls();
}

The ListBox is defined as a protected property of the class. In the CreateChildControls method, the ListBox is created and initialized. The current value of the filter is fetched from the Text property and split into an array. All groups of the site are iterated and added to the list box. Using the array of the current selection, you use a lambda method to check whether the item should be selected. Finally, you add the ListBox to the control collection.

To get this to work, you have to do one final thing: Push back the value to the WebPartManager via the Text property. Do this by modifying the web control so that it also implements the IPostBackDataHandler interface, which is defined in the System.Web.UI namespace. This handler catches the data from the postback and updates the current state of the control:

public class SiteGroupsFilterControl:
    WebControl,
    IToolPaneControl,
    IPostBackDataHandler

The IPostBackDataHandler interface has two methods; you need to use one. Using the LoadPostData method, you can intercept the postback values and update the Text property. The RaisePostDataChangedEvent is left empty, as in listing 5.16.

Listing 5.16. The LoadPostData method
public bool LoadPostData(string postDataKey,
    System.Collections.Specialized.NameValueCollection postCollection) {

    this.EnsureChildControls();

    var id = from key in postCollection.Keys.Cast<string>()
        where key.EndsWith("$" + listBox.ID)
        select key;

    if (id.First() != null) {
        this.Text = postCollection[id.First()];
        if (this.Text == null) {
            this.Text = String.Empty;
        }
    }
    return true;
}

public void RaisePostDataChangedEvent() {
}

The LoadPostData method makes sure that the child controls collection is created and then locates the postback id for the list box control. This is done using a LINQ query that finds the control ending with the id of the list box. The $ sign is used by ASP.NET to generate the unique postback names for the controls in the control tree. Each control is given the id of their parent control, which is appended with the $ sign and then the id of the actual control. The value of the list box that was posted back to the control is then set to the Text property.

To inform the ASP.NET runtime that you want to catch the postback data, you first have to register it. The registration is done in the overridden OnPreRender method. Override the OnPreRender method and add the following:

this.Page.RegisterRequiresPostBack(this);

This is all the logic necessary for your runtime filter. Next you need to register it in SharePoint.

5.5.4. Register the runtime filter

The registration of the runtime filter is done in the web.config file in the SharePoint section with the RuntimeFilter element. To finish this sample, so that you can take the complete code and put it to work, you’ll create a Feature receiver for registering the runtime filter. The Feature receiver will add the necessary XML to web.config when the Feature is activated and remove it when deactivated.

First you need to add a Feature to your project. Name the Feature SiteGroupsFilter and then add a more descriptive title and description using the Feature designer. Set the scope to WebApplication because it will affect web.config and all sites using that specific web application. Once you’ve finished with the following steps and deployed this solution, using Central Administration you can turn this filter on and off for the different web applications.

Now, add the Feature receiver to the project by right-clicking on the Feature and selecting Add Event Receiver. This will add a new class to your project, under the Feature. You must override the FeatureActivated and FeatureDeactivating methods, as in listing 5.17.

Listing 5.17. The feature event receiver for the runtime filter

The Feature receiver by default contains a set of methods that are commented out when created by Visual Studio. Uncomment the FeatureActivated and FeatureDeactivating methods. The Feature receiver applies the web.config modification when the feature is activated and removes it when deactivated . The modification takes place in the custom method. This method creates a web.config modification object for the RuntimeFilter element. The advantage of using the modification objects compared to editing the XML in web.config is that the modification objects will be replicated over all frontend servers in the farm. The element is defined using the full assembly and class names.

That was the complete walkthrough of creating a runtime filter. All that’s left is to deploy and test it.

5.5.5. Use the filter

To deploy and test your runtime filter, you press F5 and wait for the operations to complete. Open a Web Part page and add a Web Part to it. Then open the properties of the Web Part and in the tool pane go to the Advanced section. Your filter will show the groups in your site (see figure 5.12).

Figure 5.12. The custom runtime filter can be used to target Web Parts to specific groups within the site collection.

Using this simple Feature, runtime filter, and control, you have a working solution for targeting your Web Parts to groups in your site collection. You could further enhance this filter by adding a button to clear the current filter or add an Everyone group as a default.

5.5.6. Overriding the default Target Audiences filter

SharePoint Server comes with a standard runtime filter using audiences to filter the Web Parts. Each web application can have only one filter, so if you need a custom filter you have to replace the standard filter with your custom one.

To enhance the standard SharePoint Server filter, you can subclass the out-of-the-box filter. The web.config file in a SharePoint Server web application will look like this:

    <RuntimeFilter
    Assembly="Microsoft.Office.Server, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"
    Class="Microsoft.Office.Server.Audience.AudienceManager"
    BuilderURL="audience_chooser.aspx" />

As you can see, this standard definition has an extra attribute called BuilderURL. This attribute was used in previous versions of SharePoint and is no longer needed.

5.6. Summary

This concludes the chapter on Web Part properties, one of the most important and interesting topics you’ll encounter when creating reusable self-contained applications. You’ve seen how easy it is to add a property and tag it with a set of attributes to have SharePoint automatically generate an interface for editing the Web Part properties. Using these attributes, you can control how and when the properties are edited and stored. If you need to configure or control the editing of properties with, for example, validation, you can use custom Editor Parts. Editor Parts also allow you to create properties that are dependent on one another. A configurable Web Part with sufficient options will greatly increase the productivity of the users.

You also looked at complex properties that by default can’t be edited with the web interface or SharePoint Designer. By leveraging some of the .NET Framework features, you can give SharePoint the ability to convert an object to and from a string representation so that it can be edited. Finally, You explored a sample where you created a filter that can target Web Parts to groups within SharePoint. Content targeting is important in modern applications and will make your applications more usable.

This was a long chapter filled with samples and code that you can use in your applications. Next up on the agenda is how you can enhance your Web Part and its properties with localization and resources and how you can store generic application configurations.