Creating a Custom Web Part Editor in SharePoint 2010

Summary:  Learn about the Microsoft SharePoint 2010 Web Part framework and how to create a custom Web Part editor in SharePoint 2010. You can use custom editor parts to offer users functionality that can be achieved through ASP.NET user controls.

Applies to: Business Connectivity Services | Open XML | SharePoint Designer 2010 | SharePoint Foundation 2010 | SharePoint Online | SharePoint Server 2010 | Visual Studio

Provided by:  Ravi Chinni, Microsoft Corporation | Vijay Bikka, Microsoft Corporation

Contents

  • Overview of Custom Editor Parts in SharePoint 2010

  • Types for the Editor Part Framework in SharePoint 2010

  • Example Scenario: Creating a Custom Editor Part in SharePoint 2010

  • Creating the Custom Web Part Solution in Visual Studio 2010

  • Deploying the SharePoint Web Part Editor Solution

  • Conclusion

  • Additional Resources

Click to get code Download the code: Creating a Custom Web Part Editor in SharePoint 2010

Overview of Custom Editor Parts in SharePoint 2010

The Microsoft SharePoint 2010 Web Part framework enables users to configure a Web Part through Web Part properties. A default user interface (UI) is given for each Web Part property that is exposed to the user in the Edit Tool pane of the Web Part. For example, a property of type String is rendered as a text box, and an Enum is rendered as a drop-down list box.

This default rendering by the framework does not allow for much customization because the user cannot get a handle to the controls that are rendered. For example, it has the following limitations:

  • Control over UI: Numeric and string types are rendered as text boxes, enumeration types are rendered as a drop-down list box, and Boolean types are rendered as check boxes. So, you cannot show an enumeration type as radio buttons instead of a drop-down list box.

  • Events: Events that are raised by the controls cannot be handled; for example, you cannot enable or disable a drop-down list based on selecting a check box.

  • Validation: If user input has to be validated, the framework does not provide a way to easily attach a Microsoft ASP.NET validation control to the input control that is generated. For example, you cannot validate a string input for a valid email address through an ASP.NET RegEx validation control.

You can overcome these limitations by using custom editor parts. With custom editor parts, you can offer users functionality that can be achieved through ASP.NET user controls. A Web Part can implement custom editor parts that can be loaded when the Web Part is in edit mode, expose a custom UI, handle events, and validate inputs. A Web Part can have more than one custom editor part associated with it.

This article describes the SharePoint 2010 Web Part framework in detail and provides a code sample to show how to create a custom Web Part editor in SharePoint 2010. First, we examine the framework that enables custom editor parts.

Types for the Editor Part Framework in SharePoint 2010

Two types form the core for developing custom editor parts: the IWebEditable interface and the EditorPart class.

IWebEditable Interface

You can use the IWebEditable interface to specify custom editing controls that are associated with a WebPart control. The base WebPart class already implements this, and derived classes must override the base implementation. Table 1 lists the members that must be overridden.

Table 1. IWebEditable members

Member

Description

object WebBrowsableObject { get; }

The property through which the EditorPart control gets a reference to the Web Part that is being edited.

EditorPartCollection CreateEditorParts();

This method returns the collection of EditorPart controls that are associated with the Web Part.

EditorPart Class

The EditorPart class serves as the base for a derived class that provides a custom UI in the Edit Tool pane of a Web Part. The derived class must override the members that are listed in Table 2.

Table 2. EditorPart members

Member

Description

public abstract bool ApplyChanges();

This method applies the changes that are made to the EditorPart control to the Web Part.

public abstract void SyncChanges();

The Web Part stores the values that are provided by the user. This method retrieves the values from the Web Part when they are read from the editor part.

Example Scenario: Creating a Custom Editor Part in SharePoint 2010

This section uses an example scenario to describe how to build a custom Web Part that contains a custom editor part.

The scenario involves building a Web Part that displays a set of tabs to the user. Each tab contains a title and some content, which are both entered by the user. Clicking the title displays the corresponding content. The Web Part lets the user add, edit, and delete tabs by using an editor part.

Figure 1 shows the Web Part in edit mode.

Figure 1. Tab Editor Web Part in edit mode

Tab Editor Web Part in edit mode

Software Requirements for the Custom Editor Part in SharePoint 2010 Scenario

For this scenario, you must have the following software installed on your computer:

  • Microsoft SharePoint Server 2010

  • Microsoft Visual Studio 2010

Creating the Custom Web Part Solution in Visual Studio 2010

Follow these steps to create the solution in Visual Studio 2010.

To create the Web Part solution in Visual Studio

  1. In Visual Studio 2010, click New Project, expand the SharePoint node, click 2010, and then click Empty SharePoint Project. Name the project EditorPartTab, and then click OK.

  2. Add two classes to the project named TabData and TabConfigurationEditorPart.

  3. Add a Visual Web Part to the project named TabEditorWebPart. This adds a class named TabEditorWebPart and a user control named TabEditorWebPartUserControl to the project.

  4. Rename the TabEditorWebUserControl to TabConfigurationEditorPartUserControl.

    Note

    Ensure that the class name of the user control in TabConfigurationEditorPartUserControl.ascx.cs is also renamed, and not just the file name.

  5. Change the value of the Inherits attribute of the Control directive in the TabConfigurationEditorPartUserControl.ascx code file, per the new class name.

  6. Save all the files, and ensure that the class name in the TabConfigurationEditorPartUserControl.ascx.designer.cs code file is TabConfigurationEditorPartUserControl.

The following are the components that you added to the project:

  • TabData class: Entity class that represents a tab.

  • TabEditorWebPart class: Custom Web Part class to display the tabs.

  • TabConfigurationEditorPart class: Editor part class to expose the functionality to create, edit, and delete tabs.

  • TabConfigurationEditorPartUserControl class: A user control to host the interface. This is loaded by the TabConfigurationEditorPart class.

The following sections describe in detail the code and functionality of each of these components.

TabData Class

The TabData class is an entity class that contains two properties: the Title property and the Content property. Title is the name of the tab, and Content is the content that is displayed when the tab is clicked.

The class is marked with the Serializable attribute because a collection of objects of this class is declared in the TabEditorWebPart class as a property, to persist the list of tabs.

The following code shows the TabData class, which defines two properties for the title and content.

namespace MSDN.SharePoint.Samples
{
    using System;

    [Serializable]
    public class TabData
    {
        public string Title { get; set; }
        public string Content { get; set; }
    }
}

TabEditorWebPart Class

The Web Part displays a collection of tabs. The TabEditorWebPart class should already be inheriting from the WebPart class. The Web Part declares a private variable _tabList as a collection of TabData objects. This collection is wrapped through the TabList property. The property returns an empty collection if _tabList is null. The property is also marked Personalizable, to enable users to customize the tabs.

The Web Part also declares a private variable _divTabContent of type HtmlGenericControl, to display the content of the selected tab, as shown in the following code example.

namespace MSDN.SharePoint.Samples
{
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Web.UI.HtmlControls;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;

    [ToolboxItemAttribute(false)]
    public class TabEditorWebPart : System.Web.UI.WebControls.WebParts.WebPart
    {
        // A div to display the content of the tab.
        private HtmlGenericControl _divTabContent;

        // Collection of tabs.
        private List<TabData> _tabList;

        // Property to hold the collection of tabs.
        // Set the Personalizable attribute to true, 
        // to allow for personalization of tabs by users.
        [Personalizable(true)]
        public List<TabData> TabList { 
            get
            {
                if (this._tabList == null)
                {
                    // Return an empty collection if null.
                    this._tabList = new List<TabData>();
                }

                return this._tabList;
            }  
            
            set
            {
                this._tabList = value;
            } 
        }       
    }
}

To implement the IWebEditable interface

  1. As explained earlier, the Web Part must override the members of IWebEditable interface. The WebBrowsableObject property is overridden to return a reference to the TabEditorWebPart instance, as shown in the following example.

    public override object WebBrowsableObject
    {
      // Return a reference to the Web Part instance.
      get { return this; }
    }
    
  2. The CreateEditorParts method is overridden to return a collection of TabConfigurationEditorPart objects. In this case, the collection contains only one object.

    Note

    The ID of each editor part on the page should be unique, so prefix the ID of the EditorPart with the ID of the Web Part.

    public override EditorPartCollection CreateEditorParts()
    {
      TabConfigurationEditorPart editorPart = new TabConfigurationEditorPart();
    
      // The ID of the editor part should be unique. So prefix it with the ID of the Web Part.
      editorPart.ID = this.ID + "_TabConfigurationEditorPart";
    
      // Create a collection of editor parts and add them to the EditorPart collection.
      List<EditorPart> editors = new List<EditorPart> { editorPart };
      return new EditorPartCollection(editors);
    }
    

To create the controls and event handlers

  1. The CreateChildControls method renders the TabList as an ASP.NET menu control. If the number of tabs is more than zero, the first tab's title and content are shown. If the number of tabs is not more than zero, a message appears that tells the user to go to Edit mode and add tabs. The method also registers an event handler to handle the click of a tab, as shown in this code example.

    protected override void CreateChildControls()
    {
      this._divTabContent = new HtmlGenericControl("div");
      this._divTabContent.ID = "divTabContent";
    
      // Render the tabs if the count is more than zero.
      if (this.TabList.Count > 0)
      {
        Menu tabMenu = new Menu();
    
        tabMenu.Orientation = Orientation.Horizontal;
        tabMenu.MenuItemClick += new MenuEventHandler(TabMenu_MenuItemClick);
        foreach (TabData tab in this.TabList)
        {
          tabMenu.Items.Add(new MenuItem(tab.Title));
        }
    
        this.Controls.Add(tabMenu);
    
        // Set the text in div to the content of the first tab.
        this._divTabContent.InnerHtml = this.TabList[0].Content;
      }
      else
      {
        // If no tabs exist, set the div text to
        // tell the user to edit the Web Part and add tabs.
        this._divTabContent.InnerHtml = "Edit this Web Part to add Tabs.";
      }
    
      this.Controls.Add(_divTabContent);
    }
    
  2. The TabMenu_MenuItemClick event handler handles the click of a tab's title to display the corresponding content, as shown in the following code.

    private void TabMenu_MenuItemClick(object sender, MenuEventArgs e)
    {
      // Display the content related to the tab that was clicked.
      string tabTitle = e.Item.Text;
    
      // Find the tab based on the title clicked, 
      // and set the div to the corresponding content.
      this._divTabContent.InnerHtml = this.TabList.Find(t => t.Title.Equals(tabTitle)).Content;
    }
    

The SaveChanges method is intended to be called from the EditorPart class. This method calls the SetPersonalizationDirty method of the WebPart class, which sets a flag that indicates to the Web Part that the Web Part properties were changed from outside the class. If this is not set, the changes to the Web Part properties from outside the class do not take effect.

public void SaveChanges()
{
  // This method sets a flag indicating that the personalization data has changed.
  // This enables the changes to the Web Part properties from outside the Web Part class.
  this.SetPersonalizationDirty();
}

TabConfigurationEditorPart Class

The TabConfigurationEditorPart class provides the functionality to add, create, and edit the tabs. The TabConfigurationEditorPart should be inherited from the EditorPart class.

All the controls necessary to enable this functionality can be declared as the child controls in the EditorPart class itself. The TabConfigurationEditorPartUserControl was created and all the necessary child controls were declared in it. The user control is loaded by the editor part.

The class declares reference variables to the Web Part and the user control, and it declares a collection TabList of the TabData class. The TabList collection acts as the intermediary to sync the changes between the user control and the Web Part. The following code demonstrates the TabConfigurationEditorPart class, which provides the functionality to add, create, and edit the tabs.

namespace MSDN.SharePoint.Samples
{
    using System;
    using System.Collections.Generic;
    using System.Web.UI.WebControls.WebParts;
    using Microsoft.SharePoint.WebPartPages;

    public class TabConfigurationEditorPart : EditorPart
    {
        // The deployment path of the user control.
        const string TabControlConfigurationPath = @"~/_CONTROLTEMPLATES/EditorPartTab/TabEditorWebPart/TabConfigurationEditorPartUserControl.ascx";

        // The user control object ID.
        const string UserControlID = "OperationsUserControl";

        // Declare a reference to the user control.
        TabConfigurationEditorPartUserControl configuratorControl;

        // Declare a reference to the Tab Editor Web Part.
        private TabEditorWebPart tabEditorWebPart;

        public TabConfigurationEditorPart()
        {
            this.Title = "Tab Configuration";
        }

        public List<TabData> TabList { get; set; }        
    }
}

To create the controls and event handlers

  1. The EditorPart overrides the CreateChildControls method of the base class to load the user control and add it to the child control collection of the EditorPart. The method also registers an event handler to handle the event when the Cancel button is clicked in the tool pane. The user control contains certain validations, and these validations are not wanted when the Cancel button is clicked in the tool pane. So, the CreateChildControls method disables validations for the Cancel button.

    protected override void CreateChildControls()
    {
      // Get a reference to the Edit Tool pane.
      ToolPane pane = this.Zone as ToolPane;
      if (pane != null)
      {
        // Disable the validation on Cancel button of ToolPane.
        pane.Cancel.CausesValidation = false;
        pane.Cancel.Click += new EventHandler(Cancel_Click);
      }
    
      // Load the user control and add it to the controls collection of the editor part.
      this.configuratorControl =
      this.Page.LoadControl(TabConfigurationEditorPart.TabControlConfigurationPath) as TabConfigurationEditorPartUserControl;
      this.configuratorControl.ID = TabConfigurationEditorPart.UserControlID;
      this.Controls.Add(configuratorControl);
    }
    
  2. The Cancel_Click method enables the functionality to roll back the changes that were done to the TabList while the Web Part is being edited. It does this by setting the TabList property on the Web Part to that of the OriginalTabList from the user control.

    void Cancel_Click(object sender, EventArgs e)
    {
      // On cancel, roll back all the changes by restoring the original list.
      if (this.configuratorControl.OriginalTabList != null)
      {
        this.TabList = this.configuratorControl.OriginalTabList;
        this.ApplyChanges();
      }
    }
    

To override the EditorPart members

  1. The ApplyChanges method is called by the Microsoft .NET Framework when the OK button is clicked in the tool pane. Here you should write any code that updates the Web Part based on the changes that are made in the editor part.

    In the TabConfigurationEditorPart class, the overridden ApplyChanges method sets the TabList property on the Web Part to that of the one from the editor part.

    Note

    The method calls the SaveChanges method of the Web Part to notify the Web Part of the change to the TabList Web Part property.

    public override bool ApplyChanges()
    {
      this.tabEditorWebPart = this.WebPartToEdit as TabEditorWebPart;
    
      // Set the Web Part's TabList.
      this.tabEditorWebPart.TabList = this.TabList;
    
      // Call the Web Part's personalization dirty.
      this.tabEditorWebPart.SaveChanges();
      return true;
    }
    
  2. The SyncChanges method must contain the logic to read the values of the Web Part properties from the Web Part. The EditorPart class uses these values to load the state of its child controls when the Web Part goes into edit mode.

    In the TabConfigurationEditorPart class, the overridden SyncChanges method reads the TabList property on the Web Part, and assigns it to the TabList on the Editor Part.

    public override void SyncChanges()
    {
      // Sync with the new property changes here.
      EnsureChildControls();
    
      this.tabEditorWebPart = this.WebPartToEdit as TabEditorWebPart;
    
      // Read the TabList from the Web Part.
      this.TabList = this.tabEditorWebPart.TabList;
    }
    

TabConfigurationEditorPartUserControl Class

The user control provides the interface to let users add new tabs, and edit and delete existing tabs. It also enforces certain validations on the tabs. The title of the tab is required, and two tabs cannot have the same title.

The user control also displays any changes to the TabList immediately on the Web Part, by passing the updated TabList to the Web Part. When the Cancel button in the Edit Tool pane is clicked, these changes are rolled back. The following explains the markup and the code behind the user control.

The .ascx file that contains the markup declares the UI and validation controls for the user control. It has two panel controls:

  • The panelConfiguredTabs panel contains a drop-down list that displays the titles of the existing tabs. It also contains three buttons, one button each for adding, editing, and deleting a tab. Each button contains respective event handlers.

  • By default, the panelTabItem panel is hidden and becomes visible when the user tries to add or edit a tab. It contains text boxes for the title and the content of the tab. Three buttons are declared: Save, SaveOnEdit, and Cancel. The Save button is visible when the user clicks the Add New Tab button, and the corresponding event handler saves the new tab. The SaveOnEdit button is visible when the Edit button is clicked. The Cancel button is also only visible when the user tries to add or edit a tab. The user control also declares two validation controls: a required field validator on the Title text box, and a custom validator to check whether a title with the same name already exists.

The user control also declares a hidden field with the name hiddenFieldDetectRequest, which is explained in To handle the page load event later in this article. The following code example shows how to create the two panels, buttons, and validation controls.

<%@ Register 
  TagPrefix="asp" 
  Namespace="System.Web.UI" 
  Assembly="System.Web.Extensions, 
            Version=3.5.0.0, 
            Culture=neutral, 
            PublicKeyToken=31bf3856ad364e35" %>
<%@ Control 
  Language="C#" 
  AutoEventWireup="true" 
  CodeBehind="TabConfigurationEditorPartUserControl.ascx.cs"
  Inherits="MSDN.SharePoint.Samples.TabConfigurationEditorPartUserControl, 
            MSDN.SharePoint.Samples, 
            Version=1.0.0.0, 
            Culture=Neutral, 
            PublicKeyToken=3aae0e8a62e006af" %>
<asp:HiddenField runat="server" ID="hiddenFieldDetectRequest" Value="0" />
<div style="width: 250px">
  <table cellpadding="5px" cellspacing="5px">
    <tr>
      <td>
        <fieldset title="Configured Tabs">
          <legend>Configured Tabs</legend>
          <asp:Panel ID="panelConfiguredTabs" runat="server">
            <table cellpadding="5px" cellspacing="5px">
              <tr style="padding-top: 5px; padding-bottom: 5px">
                <td>
                  <asp:Label 
                    runat="server" 
                    ID="labelConfiguredTabs" 
                    Text="Tab list:"></asp:Label>
                </td>
                <td colspan="2" align="right">
                  <asp:DropDownList 
                    ID="dropDownConfiguredTabs" 
                    runat="server" 
                    EnableViewState="true"
                    AutoPostBack="true" 
                    OnTextChanged="dropDownConfiguredTabs_OnTextChanged">
                    <asp:ListItem Text="Select one" Value="Select one"></asp:ListItem>
                  </asp:DropDownList>
                </td>
              </tr>
              <tr style="padding-top: 5px">
                <td align="center">
                  <asp:Button 
                    runat="server" 
                    ID="buttonAddNewTab" 
                    Text="Add New" 
                    OnClick="ButtonAddNewTab_OnClick"
                    CausesValidation="false" />
                </td>
                <td align="center">
                  <asp:Button 
                    runat="server" 
                    ID="buttonEditTab" 
                    Text="Edit" 
                    OnClick="ButtonEditTab_OnClick"
                    CausesValidation="false" />
                </td>
                <td align="center">
                  <asp:Button 
                    runat="server" 
                    ID="buttonDeleteTab" 
                    Text="Delete" 
                    OnClick="ButtonDeleteTab_OnClick"
                    CausesValidation="false" />
                </td>
              </tr>
            </table>

          </asp:Panel>
        </fieldset>
      </td>
    </tr>
    <tr>
      <td>
        <asp:Panel ID="panelTabItem" runat="server" Visible="false">
          <fieldset title="Tab Data" runat="server" id="fieldSetTitle">
            <legend runat="server" id="legendTitle">Tab Data</legend>
            <table>
              <tr>
                <td>
                  <asp:Label ID="labelName" Text="Title:" runat="server"></asp:Label>
                </td>
                <td>
                  <asp:TextBox ID="textBoxTitle" runat="server" MaxLength="20"></asp:TextBox>
                  <asp:Label runat="server" ForeColor="Red" Text="*" />
                </td>
              </tr>
              <tr>
                <td>
                  &nbsp;
                </td>
                <td>
                  <asp:RequiredFieldValidator 
                    runat="server" 
                    ControlToValidate="textBoxTitle" 
                    ErrorMessage="Title is required."
                    ID="requiredFieldValidatorTextBoxTitle" 
                    EnableClientScript="true"></asp:RequiredFieldValidator>
                  <br />
                  <asp:CustomValidator 
                    runat="server" 
                    ControlToValidate="textBoxTitle" 
                    ErrorMessage="Tab with same Title already exists."
                    ID="customValidatorTextBoxTitle"></asp:CustomValidator>
                </td>
              </tr>
              <tr>
                <td>
                  <asp:Label 
                    ID="labelURL" 
                    Text="Content:" 
                    runat="server"></asp:Label>
                </td>
                <td>
                  <asp:TextBox 
                    ID="htmlBody" 
                    runat="server" 
                    TextMode="MultiLine" 
                    Rows="6" 
                    MaxLength="100"></asp:TextBox>
                </td>
              </tr>
              <tr>
                <td colspan="2" align="right">
                  <asp:Button 
                    runat="server" 
                    ID="buttonSave" 
                    Text="Save" 
                    OnClick="ButtonSave_Click"
                    CausesValidation="true" 
                    Visible="false" />
                  <asp:Button 
                    runat="server" 
                    ID="buttonCancel" 
                    Text="Cancel" 
                    OnClick="ButtonCancel_Click"
                    CausesValidation="false" />&nbsp;&nbsp;
                  <asp:Button 
                    runat="server" 
                    ID="buttonSaveOnEdit" 
                    Text="Save" 
                    OnClick="ButtonSaveOnEdit_Click"
                    CausesValidation="true" 
                    Visible="false" />
                </td>
              </tr>
            </table>

          </fieldset>
        </asp:Panel>
      </td>
    </tr>
  </table>

</div>

The .ascx.cs file that contains the code-behind contains the logic to save and roll back the changes that are made to the tab list. The TabConfigurationEditorPartUserControl class in the code-behind file declares references to the TabConfigurationEditorPart collection of TabData classes.

The following code example shows the namespaces that are used by the .ascx.cs code file and creates several objects for the TabConfigurationEditorPartUserControl class.

namespace MSDN.SharePoint.Samples
{
  using System;
  using System.Collections.Generic;
  using System.Linq;

  public partial class TabConfigurationEditorPartUserControl : System.Web.UI.UserControl
  {
    private TabConfigurationEditorPart parentEditorPart;
        
    public List<TabData> TabList;

    const string TabStorageViewStateId = "TabStorageViewState";   
  }
}

The following sections describe other methods and properties that the class requires.

To handle the Page Load event

  1. The Page_Load method retrieves the current set of tabs from the Web Part storage by calling the SyncChanges method of the EditorPart class.

    After you retrieve the list of the tabs, you must bind the tab titles to the drop-down list control. This must be done the first time that the control is loaded. Traditionally you do this by selecting the IsPostBack property of the Page control. If the IsPostBack property is false, the page is being loaded for the first time, and the initial data binding is done on that condition. But the editor part controls are loaded for the first time when the user clicks the Modify Web Part menu on the Web Part or takes the page to Edit mode. Both of these actions are postbacks, and the IsPostBack property would be true. All the subsequent actions on the editor part would be postbacks, and the property would continue to be true. Therefore, you cannot use this property for any logic to be executed on the first load of the control.

  2. To work around this, use the hiddenFieldDetectRequest variable. The first time that the user control is loaded, it has a default value of 0, and the data binding is done on this condition. Also, the value is set to 1 to avoid the repeated execution of the same code on subsequent postbacks. The code to save the list of the tabs into the view state is executed on the same condition, to enable the cancel functionality. When the user clicks the Cancel button on the tool pane, the user’s changes to the tabs are rolled back: the tab list on the Web Part is restored to the one that was stored in the view state. This is demonstrated in the following code example.

    protected void Page_Load(object sender, EventArgs e)
    {
      this.parentEditorPart = this.Parent as TabConfigurationEditorPart;
    
      // Call Sync Changes on the editor part, to read the tab list from the Web Part.
      this.parentEditorPart.SyncChanges();
      this.TabList = this.parentEditorPart.TabList;
    
      // Check whether this is the first Page_Load of the control.
      if (this.hiddenFieldDetectRequest.Value == "0")
      {
        this.hiddenFieldDetectRequest.Value = "1";
    
        // Save the original tab list to the control's ViewState.
        this.SaveOriginalTabListToViewState();
    
        // Bind the tab list to the drop-down.
        this.PopulateConfiguredTabsDropDown();
      }
    }
    
  3. The SaveOriginalTabListToViewState method saves the tab list to the view state of the user control, as shown in the following code.

    private void SaveOriginalTabListToViewState()
    {
      // Save the tab list that was already retrieved 
      // from the Web Part storage to the view state.
      if (this.TabList != null)
      {
        this.ViewState[TabStorageViewStateId] = this.TabList;
      }
    }
    
  4. The PopulateConfiguredTabsDropDown method does the data binding for the drop-down list. The binding is done either the first time that the control is loaded or when the Save or SaveOnEdit buttons are clicked. Each time the binding is done, the drop-down list's selected item is set to the default value Select One. So, the method disables the Edit and Delete buttons and hides the Tab Item panel.

    private void PopulateConfiguredTabsDropDown()
    {
      this.dropDownConfiguredTabs.Items.Clear();
      this.dropDownConfiguredTabs.Items.Add("Select one");
    
      // Add the Tab titles to the tab list drop-down.
      if (this.TabList != null)
      {
        for (int index = 0; index < TabList.Count; index++)
        {
          this.dropDownConfiguredTabs.Items.Add(TabList[index].Title.ToString());
        }
      }
    
      // Hide the Edit and Delete buttons, and tab panel.
      this.buttonDeleteTab.Enabled = false;
      this.buttonEditTab.Enabled = false;
      this.panelTabItem.Visible = false;
    }
    
  5. The OriginalTabList property returns the list of tabs saved to the view state.

    public List<TabData> OriginalTabList
    {
      get
      {
        // Retrieve the original tab list from the ViewState.
        List<TabData> retValue = null;
        retValue = this.ViewState[TabStorageViewStateId] as List<TabData>;
        return retValue;
      }
    }
    

To handle the tab list drop-down change event

  • This event handler handles the change to the tab list drop-down. It sets the state of the Edit and Delete buttons in the Tab Item panel according to the drop-down value selected. The buttons are disabled and the panel is hidden when the selected value is Select One, as shown in the following code example.

    protected void dropDownConfiguredTabs_OnTextChanged(object sender, EventArgs e)
    {
      this.panelTabItem.Visible = false;
    
      // Enable the Edit and Delete buttons if any tab is selected in the drop-down list.
      if(dropDownConfiguredTabs.SelectedIndex == 0)
      {
        this.buttonDeleteTab.Enabled = false;
        this.buttonEditTab.Enabled = false;
      }
      else
      {
        this.buttonDeleteTab.Enabled = true;
        this.buttonEditTab.Enabled = true;
      }
    }
    

To handle the Add, Edit, and Delete button click events

  1. The event handler for the Add New button click shows the Tab Item panel and the Save button, clears the Title and Content text boxes, and hides the SaveOnEdit button, as shown in the following example.

    protected void ButtonAddNewTab_OnClick(object sender, EventArgs e)
    {
      // Show the panel and clear title and content.
      this.panelTabItem.Visible = true;
      this.textBoxTitle.Text = string.Empty;
      this.htmlBody.Text = string.Empty;
      this.buttonSave.Visible = true;
      this.buttonSaveOnEdit.Visible = false;
    }
    
  2. The event handler for the Edit button click shows the tab item panel and the SaveOnEdit button, sets the values of the Title and Content text boxes to the values of the selected tab, and hides the Save button.

    protected void ButtonEditTab_OnClick(object sender, EventArgs e)
    {
      // Hide the Save button and Show the SaveOnEdit button.
      this.buttonSave.Visible = false;
      this.buttonSaveOnEdit.Visible = true;
    
      // Show the tab panel.
      this.panelTabItem.Visible = true;
    
      // Find the tab selected to Edit, and set the Title and Content text box values.
      TabData selectedLink = this.TabList.First(tab => tab.Title == this.dropDownConfiguredTabs.SelectedItem.Text);
      this.textBoxTitle.Text = selectedLink.Title;
      this.htmlBody.Text = selectedLink.Content;
    }
    
  3. The event handler for the Delete button removes the selected tab from the list of tabs, re-binds the tab list drop-down, and calls ApplyChanges method to have the changes applied to the Web Part.

    protected void ButtonDeleteTab_OnClick(object sender, EventArgs e)
    {
      // Remove the title from the drop-down list.
      this.TabList.RemoveAll(tab => tab.Title == this.dropDownConfiguredTabs.SelectedItem.Text);
    
      // Re-bind the drop-down list.
      this.PopulateConfiguredTabsDropDown();
    
      // Apply the changes to the Web Part.
      this.ApplyChanges();
    }
    

To handle the Save, SaveOnEdit, and Cancel button clicks

  1. In the following code, the event handler for the Save button click checks whether a tab with the same title already exists. If there is another tab with that title, the validation fails. If a tab with the same title does not exist, a new tab is created and added to the tab list. The code then re-binds the drop-down list and calls the ApplyChanges method to have the changes applied on the Web Part.

    protected void ButtonSave_Click(object sender, EventArgs e)
    {
      // Check whether a tab with same title already exists.
      IEnumerable<TabData> matches = this.TabList.Where(tab => (tab.Title == this.textBoxTitle.Text));
    
      if (matches.Count() > 0)
      {
        // Fail the validation if a tab with the same title already exists.
        this.customValidatorTextBoxTitle.IsValid = false;
      }
      else
      {
        // If no tab exists with the same title, save and apply the changes.
        this.SaveTab();
        this.PopulateConfiguredTabsDropDown();
        this.ApplyChanges();
      }
    }
    
  2. The event handler for the SaveOnEdit button first checks whether the edited title of the selected tab in the drop-down list already exists; if it does, the validation fails. If the title was not edited, it removes the selected tab from the tab list, creates a new tab using the title and content, and adds it to the tab list. Also, the tab list drop-down is re-bound, and the ApplyChanges method is called to apply the changes to the Web Part.

    protected void ButtonSaveOnEdit_Click(object sender, EventArgs e)
    {
    
      // If the selected item's text in the tab list drop-down is not equal
      // to the value in the Title text box, the title has been modified. 
      // Check whether the modified title already exists.
      if((this.dropDownConfiguredTabs.SelectedItem.Text != this.textBoxTitle.Text) &&
       (this.TabList.Where(tab => tab.Title == this.textBoxTitle.Text).Count() > 0 ))
      {
    
        // If the edited title of the existing tab matches an already existing one, fail the validation.
        this.customValidatorTextBoxTitle.IsValid = false;
      }
      else
      {
    
        // If the title wasn't edited, remove the existing tab.
        // This line doesn’t do anything if a tab with the edited title doesn't already exist.
        this.TabList.RemoveAll(link => link.Title == this.dropDownConfiguredTabs.SelectedItem.Text);
    
        // Create a new tab with the edited title and content, and add it to the collection.
        this.SaveTab();
    
        // Rebind the drop-down list.
        this.PopulateConfiguredTabsDropDown();
    
        // Call Apply Changes to display the changes on the Web Part.
        this.ApplyChanges();
      }
    }
    
  3. The Cancel button event handler just hides the Tab Item panel.

    protected void ButtonCancel_Click(object sender, EventArgs e)
    {
      // Hide the tab panel.
      this.panelTabItem.Visible = false;
    }    
    
  4. The SaveTab utility method creates a new tab from the currently entered title and content, and adds it to the tab list.

    private void SaveTab()
    {
      // Create a new tab, and save it to the tab list collection.
      TabData tab = new TabData();
      tab.Title = this.textBoxTitle.Text;
      tab.Content = this.htmlBody.Text;
      this.TabList.Add(tab);
    }
    
  5. The ApplyChanges method calls the ApplyChanges method of the editor part, which applies the changes to the Web Part.

    private void ApplyChanges()
    {
      // Call the ApplyChanges method of the parent editor part class.
      this.parentEditorPart.ApplyChanges();
    }
    

Deploying the SharePoint Web Part Editor Solution

To deploy the solution, in Visual Studio, right-click the project in Solution Explorer, and then click Deploy. Visual Studio deploys the solution package to the SharePoint farm.

Create a Web Parts page, and add the Web Part to the page.

Conclusion

This article discussed the SharePoint 2010 Web Part framework and how to create a custom Web Part editor. Developers are not confined to the default interface that is rendered by the Web Part framework for the Web Part properties. You can customize the interface to satisfy almost any project requirement.

Additional Resources

For more information, see the following resources: