Custom Web Part: creating and deploying an ASP.NET 2.0 web part with a custom EditorPart for SharePoint 2007 and WSS3

In this post I’ll create a sample ASP.NET 2.0 web part with a custom EditorPart. Source code is available at the bottom of this post.
If you developed SharePoint 2003 (and WSS2) web parts you might know the functionality as ToolParts and the toolpart pane. There is an article on MSDN on how to create your custom ToolPart () by using the WSS2 web part base class. Although you can still use this when developing SharePoint 2007 or WSS3 web parts it is generally recommended to use the ASP.NET 2.0 base class.
The good news is when you build an ASP.NET 2.0 web part you can use it within or without SharePoint.

What does this sample contain? The web part displays a banner image and line of text. The banner URL and a choice of a month is done via the custom web part editor.
I’m deploying the web part assembly to the bin directory (not the GAC) and setting custom CAS security, all that put together in a SharePoint solution.
The web part itself is therefore usable in any ASP.NET project but the deployment part of this post is specific to MOSS and WSS3.

Creating the Visual Studio 2005 project
To develop custom web part(s) create a new class library project. Configure the namespace and assembly name in the solution properties window.
I normally delete the default class1.cs file and create any needed files from scratch.
Open the AssemblyInfo.cs file and add the following code at the end of the file:

[assembly: System.Security.AllowPartiallyTrustedCallers()]

We need to add this attribute because the assembly will be deployed to the local bin directory and give partial trust.

Adding properties to the editor pane via attributes
By using attributes on public properties of the web part you can have editable properties (in personal or shared mode). This can be achieved by adding the following properties to a public property:

[WebBrowsable(true)]

Personalizable(PersonalizationScope.Shared)]

public string BannerUrl

{

  get { return _bannerUrl; }

  set { _bannerUrl = value; }

}

This will automatically add any strings as textboxes in the toolpart pane, in the section Miscellaneous.
You can use these attributes for simple properties where you don’t require validation. String and int will automatically show a TextBox control. Enums will show in the form of a dropdown list.

Sample of how properties are automatically rendered in edit mode:

webpart_edit_properties

Implementing CreateChildControls and RenderContents
In my sample I’m overriding the CreateChildControls method to create constituent controls and then RenderContents method to render them to the page. There is some HTML being written to the page, you may want something fancier than a table so this is just a simple example.

protected override void CreateChildControls()

        {

            base.CreateChildControls();

 

            imageBanner = new Image();

            labelMonth = new Label();

            labelError = new Label();

 

            if (string.IsNullOrEmpty(_bannerUrl) || _month == 0)

            {

                labelError.Text = Properties.Resources.webpart_configuration;

            }

            else

            {

                imageBanner.ImageUrl = _bannerUrl;

                DateTime month = new DateTime(DateTime.Now.Year, _month, 1);

                labelMonth.Text = string.Format(Properties.Resources.editor_monthlabel, month.ToString("MMMM"));

                labelMonth.Font.Bold = true;

                labelMonth.Font.Size = FontUnit.XLarge;

            }

 

            //set ChildControlsCreated to true so that ASP.NET does not call method twice

            this.ChildControlsCreated = true;

        }

 

        protected override void RenderContents(System.Web.UI.HtmlTextWriter writer)

        {

            if (string.IsNullOrEmpty(labelError.Text))

            {

                writer.Write("<table border=1><tr><td>");

                imageBanner.RenderControl(writer);

                writer.WriteLine("</td></tr>");

                writer.WriteLine("<tr><td");

                labelMonth.RenderControl(writer);

                writer.WriteLine("</td></tr></table>");

 

            }

            else {

                labelError.RenderControl(writer);

            }

 

            this.EnsureChildControls();

        }

Adding a resource file
As this web part may be implemented in a different language at a later stage I normally add a resources file for any text used in the control.
Go to Project properties in Visual Studio 2005 and click the Resources item in the left. Click on the text to create a new resource file.

webpart_resources

Any strings used in the control can now be entered here. Accessing the strings in the file can be done like this:

this.Title = Properties.Resources.editor_title; 

This is best the approach when building the web part to be available in multiple languages. Always create a base resources file “Resources.resx” and then add needed languages in the following format: Resources.[language]-[COUNTRY].resx (Resources.en-US.resx, Resources.nl-NL.resx).

Creating the EditorPart class
By creating a custom EditorPart you can choose if you want to show checkboxes, dropdowns, textboxes or other types of controls. You may also add validation for required fields.

Add a new class to the project, in my sample: BannerEditorPart.cs

This control inherits from the base class System.Web.UI.WebControls.WebParts.EditorPart.
Implement the constructor:

public class BannerEditorPart : System.Web.UI.WebControls.WebParts.EditorPart

    {

        public BannerEditorPart()

        {

            this.ID = "BannerEditor";

            this.Title = "Banner properties";

        }

 

    } 

Note: be sure to set the ID property of the EditorPart. Although this does not seem to be a required property in ASP.NET, it will return an error when going to edit mode in a SharePoint web.

Override CreateChildControls() and RenderContents() as in the web part. Two extra methods are overriden in order to save changes to the properties.

public override void SyncChanges()

        {

 

            EnsureChildControls();

            BannerWebpart editorPart = WebPartToEdit as BannerWebpart;

            if (editorPart != null)

            {

                textImageUrl.Text = editorPart.BannerUrl;

                if (editorPart.Month != 0)

                {

                    dropdownMonth.SelectedValue = editorPart.Month.ToString();

                }

            }

        }

 

        public override bool ApplyChanges()

        {

 

            EnsureChildControls();

            BannerWebpart editorPart = WebPartToEdit as BannerWebpart;

            if (editorPart != null)

            {

                try

                {

                    editorPart.BannerUrl = textImageUrl.Text;

                    editorPart.Month = Convert.ToInt32(dropdownMonth.SelectedValue);

                }

                catch (System.Exception exc)

                {

                    _displayErrorMessage = true;

                    error = "Error: " + exc.Message;

                    return false;

                }

            }

            return true;

        }

Final step is to implement the IWebEditable interface on the web part BannerWebpart class and implement the CreateEditorParts method and the WebBrowsableObject property.

object IWebEditable.WebBrowsableObject

        {

            get { return this; }

        }

 

        EditorPartCollection IWebEditable.CreateEditorParts()

        {

            if (this.WebPartManager.Personalization.Scope == PersonalizationScope.Shared)

            {

                List<EditorPart> customEditorPartCollection = new List<EditorPart>();

                customEditorPartCollection.Add(new Demo.Webparts.BannerEditorPart());

                EditorPartCollection editorPartCollection = new EditorPartCollection(customEditorPartCollection);

                return editorPartCollection;

            }

            else

            {

                return null;

            }

        }

Building the manifest.xml and SharePoint solution (.wsp)

Strong name the assembly: go to Project Properties > Signing > Sign the assembly.

The SharePoint solution will install the web part using a .webpart file. In this file we need to add the public token of the assembly. To get the public key use you can use Reflector (see Resources).
XML for the BannerWebpart.webpart file:

<?xml version="1.0" encoding="utf-8" ?>

<webParts>

      <webPart xmlns="http://schemas.microsoft.com/WebPart/v3">

            <metaData>

                  <type name="Demo.Webparts.BannerWebpart, Demo.Webparts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bc0f0ce76789c794" />

                  <importErrorMessage>Cannot import web part.</importErrorMessage>

            </metaData>

            <data>

                  <properties>

                        <property name="Title" type="string">Demo web part - banner</property>

                        <property name="Description" type="string">

                              This is a demo web part

                        </property>

                        <property name="ChromeType">None</property>

                  </properties>

            </data>

      </webPart>

</webParts>

This .webpart file needs to go into the .wsp solution file along with the assembly file. But first we need to create the manifest.xml file in which we add a <Assemblies>, <SafeControls>, and <DwpFiles> sections and the Code Access Security (CAS) elements.

<Solution xmlns='http://schemas.microsoft.com/sharepoint/' SolutionId='guidhere'>

    <Assemblies>

        <Assembly DeploymentTarget='WebApplication' Location='Demo.Webparts.dll'>

        <SafeControls>

      <SafeControl Assembly="Demo.Webparts, Version=1.0.0.0, Culture=neutral, PublicKeyToken=bc0f0ce76789c794" Namespace="Demo.Webparts" TypeName="*" Safe="True" />

</SafeControls>

 

        </Assembly>

    </Assemblies>

    <DwpFiles>

        <DwpFile Location='BannerWebpart.webpart'/>

    </DwpFiles>

<CodeAccessSecurity>

      <PolicyItem>

      <Assemblies>

                        <Assembly Name="Demo.Webparts" />

                  </Assemblies>

                  <PermissionSet class='NamedPermissionSet' Name='Demo permission set' version='1' Description='Demo web part trust'>

                        <IPermission

                    class="AspNetHostingPermission"

                    version="1"

                    Level="Medium"

                            />

                        <IPermission

                                    class="DnsPermission"

                                    version="1"

                                    Unrestricted="true"

                            />

                        <IPermission

                                    class="SecurityPermission"

                                    version="1"

                                    Flags="Assertion, Execution, ControlThread, ControlPrincipal, RemotingConfiguration, UnmanagedCode"

                            />

 

                        <IPermission class="Microsoft.SharePoint.Security.SharePointPermission, Microsoft.SharePoint.Security, Version=12.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"

                         UnsafeSaveOnGet="True" ObjectModel="True" version="1" />

 

                        <IPermission class="WebPartPermission"

                                    version="1"

                                    Connections="True"

                            />

                        <IPermission

                              class="WebPermission"

                                    version="1">

                              <ConnectAccess>

                                    <URI uri="$OriginHost$"/>

                              </ConnectAccess>

                        </IPermission>

 

                  </PermissionSet></PolicyItem>

</CodeAccessSecurity>

</Solution>

Once we have the manifest file we can build the .wsp file using MAKECAB.EXE for example. The cabinet definition (cab.ddf) file looks like this:

;

.Set CabinetNameTemplate=Demo.Webparts.wsp

.set DiskDirectoryTemplate=CDROM ; All cabinets go in a single directory

.Set CompressionType=MSZIP;** All files are compressed in cabinet files

.Set MaxDiskFileCount=1000 ; Limit file count per cabinet

.Set UniqueFiles='OFF'

.Set Cabinet=on

.Set DiskDirectory1=.

"C:\My Documents\Visual Studio 2005\Projects\Demo.Webparts\
Demo.Webparts\SOLUTION\DLLS\Demo.Webparts.dll"      "Demo.Webparts.dll"

"C:\My Documents\Visual Studio 2005\Projects\Demo.Webparts\Demo.Webparts\
SOLUTION\DwpFiles\BannerWebpart.webpart"      "BannerWebpart.webpart"

"C:\My Documents\Visual Studio 2005\Projects\Demo.Webparts\
Demo.Webparts\SOLUTION\manifest.xml"  "manifest.xml"

;*** <the end>

Build the .wsp file in command line or using a batch file:

makecab.exe /F cab.ddf

The final step is to install the solution and deploy it to the desired web application. There are two ways to do this: in command line using STSADM or using the nice tool by Mondosoft SharePoint Solution Installer .

Here is an example of the web part, very simple content but you get the idea.

webpart_editor_custom   webpart_final

Download the source code here: SampleDemoWebparts.zip

Some resources worth checking out:

ASP.NET Quickstarts tutorials – Personalizing Using Web Parts
http://quickstarts.asp.net/QuickStartv20/aspnet/doc/webparts/default.aspx

You might want to check out this blog post by Dimitri Andreev: WSS 3.0 webparts development
http://blogs.msdn.com/dmandreev/archive/2006/12/07/wss-3-0-webparts-development.aspx
Dimitri gives an overview on the most important differences of ASP.NET 2.0 web parts and SharePoint web parts (using the Microsoft.SharePoint.WebPartPages base class), as well as how to deploy your web parts.

 

Technorati tags: , , , , ,

 

 

Feedback

Posted on 13 August 2007 @ 13:05

I used the editorpart solution before (http://blogs.tamtam.nl/roels/2007/05/14/CreatingMinimalconfigurationWebparts.aspx) but I recently abandoned this approach for an even more userfriendly way of displaying settings. Now I render a fully custom settings-form when the webpart is displayed in editmode. This is of course more work than the editorpart-approach, but it's very intuitive to a non-technical webeditor.

Posted on 06 February 2008 @ 11:11

Doesn't the resources file get included in the wsp?

Posted on 06 February 2008 @ 19:43

In this case it's a DLL we are generating so you want this embedded in the assembly (and satellite assemblies).

Posted on 07 February 2008 @ 23:55

An excellent post, which helped me create my own web part - thanks. However, would you recommend adding multiple web parts into the same class library or creating a new library - thinking about ease of deployment, integration testing, etc.

Posted on 08 February 2008 @ 18:12

Nice to know it helped.
It would depend on the type of controls. It is probably a good idea to bundle several web parts in a single library, thinking about just one .wsp to set, one single CAS, one single SafeControls section. Especially if the different web parts are for one defined project.
But it really depends on the final usage/situation.

Posted on 22 February 2008 @ 10:30

Is it possible within the editor web part to have controls using postback? For example, user has dropdown to choose required list then next dropdown populates for user to choose the list item to display.

Posted on 22 February 2008 @ 16:00

Yes, it's possible to have events and postback. Just do as you would with a regular custom control.

Posted on 13 March 2008 @ 10:18

Do you need to add any special permissions to CAS or to MOSS so that anonymous users can see the output from webparts using the above methods above and beyond setting anonymous access in authentication providers, in iis and at the site level in moss?

Posted on 20 June 2008 @ 17:05

Hi, thank you for the posting. I am getting errors. could you please tell what are the different references to be added to work this code. thank you

Posted on 21 June 2008 @ 15:03

Hi,
You can view the referenced assemblies by downloading the sample project provided at the end of the post.

Katrien

Posted on 18 July 2008 @ 10:53

I can not access the Properties keyword. So Properties.Resources.editor_title does not exists.

What do I miss?

Posted on 18 July 2008 @ 11:18

Found it ;-) had some properties wrong in mine solution.
But I can not get the localization resources to work. It always get it from the general resource.

Posted on 08 May 2009 @ 15:45

Using the embedded resources method used here is it possible to have multiple resource files embedded in the assembly, i.e. for French, German, etc? Or does this need to be done differently for web parts?

Posted on 26 November 2009 @ 17:23

Hi,
Thank you for this article.
I want this kind articles URL to my mail.
Could you please send some important URLs or else MOSS C# material.

I'm really thinking of you and appreciating your effort & sharing your knowledge.Take your own time.
Thank you very much...

Please post your comments:

Name:  
Email (optional): Your email address will not be posted.
URL (optional):
Comments: HTML will be ignored, URLs will be converted to hyperlinks  
Copyright © 2007 Katrien De Graeve.