In the article Creating a SharePoint Web Part – Step by Step: Part 1 – Initial Deveopment, we created a "shell" or "skeleton" web part, installed it, and dropped it onto a SharePoint portal page, confirming that it rendered correctly. As we mentioned in that article, that initial coding and deployment seems to be the root of most difficulties with web parts, so we organize our development into two phases: the first getting the plumbing matters out of the way and the second being build-out of functionality. The second part is what we will cover in this article.
Phase 2 – Adding Functionality to the Web Part "Shell"
Now that we have successfully created and deployed the "shell" web part, we have passed the major pain point in web part development. With that tackled, let’s now do the "easy" stuff – adding real functionality. The word "easy" is in sneer quotes because I am fully aware real production web parts may have complex functionality and thus be hard to write, but in our case the functionality is simple and easy to implement.
Recall our requirement is to permit the user to specify text to be displayed in the web part, as well as the background color of the web part. We’ll add another requirement here to allow the user to specify the padding between the web part border and the text. Our general approach will be to emit HTML and use the bgcolor attribute to specify the backgound color. Although this element is deprecated in favor of specifying color using style sheets, we’ll keep this example simple and use it anyway. There are only a few elements that officially support bgcolor: <body>, <table>, <tr>, <th>, and <td>. It looks like we’ll need to emit an HTML table to support our background color requirement, and this will nicely support our padding requirement, too.
First, we need to expose two additional public properties to support the background color and padding specification. The text property is already handled by the following code:
private const string defaultText = "";
private string text = defaultText;
[Browsable(true),
Category("Miscellaneous"),
DefaultValue(defaultText),
WebPartStorage(Storage.Personal),
FriendlyName("Text"),
Description("Text Property")]
public string Text
{
get
{
return text;
}
set
{
text = value;
}
}
You can see we have constant defined specifying the default text. There is also a private field responsible for holding the specified text. The public Text property allows the text to be get and set, and is decorated with several attributes which control how the property is rendered by the SharePoint user interface. The SharePoint UI will iterate over all the public properties defined in the web part and render those with attribute Browsable equal to True. SharePoint will use the declared type of the property to decide what type of control to render and the attributes specify the label to use for the control, the tool tip description, and the default value.
For the background color, we would like the user to be able to select from a drop-down list if possible. The .NET Framework conveniently defined an enumeration of colors called KnownColor in the System.Drawing namespace. The SharePoint user interface is smart enough to detect when the data type of a public property is an enumeration and automatically renders the field as a drop-down list containing the values of the enumeration. So all we have to do is declare a property having the KnownColor type and SharePoint takes care of the rest.
First add a reference to the System.Drawing namespace at the top of the class file:
using System.Drawing;
Now add the following code after the Text property code:
private const KnownColor defaultColor = KnownColor.White;
private KnownColor backColor = defaultColor;
[Browsable(true),
Category("Miscellaneous"),
DefaultValue(defaultColor),
WebPartStorage(Storage.Personal),
FriendlyName("Background Color"),
Description("Background color for the web part")]
public KnownColor BackColor
{
get
{
return backColor;
}
set
{
backColor = value;
}
}
Now we have to add another property to allow the user to specify a padding. This will be of integer type. Add the following code after the BackColor property code:
private const int defaultPadding = 0;
private int padding = defaultPadding;
[Browsable(true),
Category("Miscellaneous"),
DefaultValue(defaultPadding),
WebPartStorage(Storage.Personal),
FriendlyName("Padding"),
Description("Padding between the web part border and the text")]
public int Padding
{
get
{
return padding;
}
set
{
padding = value;
}
}
As a final step, change the WebPartStorage from Personal to Shared on each of the three properties. They each should read:
WebPartStorage(Storage.Shared)
So at this point, if compiled and deployed to the GAC, the web part would accept property settings from the user for background color and padding, but wouldn’t do anything with them. Let’s correct that by updating the rendering code.
As you can see, the rendering code just takes the value of the Text property and spits it out into the HTML output stream:
protected override void RenderWebPart(HtmlTextWriter output)
{
output.Write(SPEncode.HtmlEncode(Text));
}
We will update the body of this function to render an HTML table with a single row and single column. The column will contain the text and have a background color set to the specified value. The table will have a cell padding set to the value specified by the user.
Here is the updated code:
protected override void RenderWebPart(HtmlTextWriter output)
{
// Emit the <table> element
output.AddAttribute( HtmlTextWriterAttribute.Cellspacing, "0" );
output.AddAttribute( HtmlTextWriterAttribute.Cellpadding, Padding.ToString( ) );
output.AddAttribute( HtmlTextWriterAttribute.Width, "100%" );
output.AddAttribute( HtmlTextWriterAttribute.Border, "0" );
output.RenderBeginTag( HtmlTextWriterTag.Table );
// Emit the <tr> element
output.RenderBeginTag( HtmlTextWriterTag.Tr );
// Emit the <td> element
output.AddAttribute( HtmlTextWriterAttribute.Bgcolor, BackColor.ToString( ) );
output.RenderBeginTag( HtmlTextWriterTag.Td );
// Emit the text
output.Write(SPEncode.HtmlEncode(Text));
// Emit the closing tags
output.RenderEndTag( ); // td
output.RenderEndTag( ); // tr
output.RenderEndTag( ); // table
}
Notice we are using the stack-based Render__ methods to emit HTML, rather than the .Write method or .Write__ methods. This is a best practice when working with the HtmlTextWriter class.
Let’s conclude by rebuilding the solution and confirming that all is well. If there are compile errors, go back and correct the code according the directions given above.
Now we have to redeploy the assembly and test the changes. You do not need to use the stsadm.exe tool again, because the web part is already registered with SharePoint – all that is needed is to replace the assembly in the GAC. To do that, use the gacutil.exe utility. Bring up the Visual Studio .Net command prompt (typically at All Programs | Microsoft Visual Studio .NET | Visual Studio .NET Tools | Visual Studio .NET 2003 Command Prompt) and enter the following command:
gacutil /i C:DevSampleWebPartsSampleWebPartsbinDebugSampleWebParts.dll /f
Then do an iisreset command to ensure the new DLL gets loaded on the next page request.
Be sure to specify the actual path to the web part assembly for the value of the /i switch. Your command window should look like:
Point your browser to the test portal page on which you dropped your web part from Part 1. Bring up the tool window and set the Miscellaneous properties, and observe the effects in the user interface of the web part.
Here is what the result will look like, along with the corresponding property settings:
One "gotcha" that I have encountered has to do with serializing/deserializing the web part to/from the database. Give thought to your public property data types before you implement, because if you change the data type of a property after you have already deployed, you will probably run into deserialization problems as SharePoint tries to deserialize old configuration data into the new schema. Also, be sure you get the data types of your defaults right. Typically, to create a new public property you will copy and paste the code from another property and it is easy to overlook something when changing the pasted code. If you don’t get the data type right, you will experience deserialization errors.