Walkthrough: Implementing Web Parts Personalization using IPersonalizable

This walkthrough demonstrates how to supply user-specific default values by implementing a user control as a personalizable Web Parts control.

The most significant characteristic of this control is that its personalizable properties are stored in non-value type variables. In this case, the values are stored in a BulletedList control. This implies that the system does not have any way to know when the user changes the values in the list. This walkthrough demonstrates how to inform the system that the values are changed so they can be preserved for future use. This can be useful if you are creating customizable online forms on a per-user basis. Web Parts personalization allows the system to recognize the user so that his or her default form values can be automatically restored on subsequent visits to the site.

NoteNote

The user control you create in this walkthrough does not inherit from the WebPart class; however, a user control can function as a WebPart control. During this walkthrough, you add a user control to a WebPartZoneBase zone. This allows ASP.NET to wrap the user control in a GenericWebPart control. The user control will then work like any other WebPart control, and will allow you to explore personalization.

During this walkthrough, you will learn how to:

  • Create a user control with personalizable properties, whose values can be saved in long-term storage.

  • Display user-specific default values.

  • Work with the user control in a zone as a true WebPart control.

NoteNote

This type of application could be developed using ASP.NET profiles. However, in this case you are not storing information about the user to be reused across an entire application, as in a shopping cart application. Instead, you are saving user-specific preferences or settings for each control, on a per-page basis. For more information about profiles, see ASP.NET Profile Properties Overview.

Prerequisites

To complete this walkthrough, you will need:

  • Internet Information Services (IIS) installed and configured on the computer that will host the site. For more information about installing and configuring IIS, see the IIS Help documentation included with the installation, or see the online IIS documentation on the Microsoft TechNet site (Internet Information Services 6.0 Technical Resources).

  • An ASP.NET Web site that can identify individual users. If you have such a site already configured, you can use that site as a starting point for this walkthrough. Otherwise, for more information about creating a virtual directory or site, see How to: Create and Configure Virtual Directories in IIS.

  • A configured personalization provider and database. Web Parts personalization is enabled by default, and it uses the SQL personalization provider (SqlPersonalizationProvider) with the Microsoft SQL Server Standard Edition (SSE) to store personalization data. This walkthrough uses SSE and the default SQL provider. If you have SSE installed, no configuration is needed. SSE is available with Microsoft Visual Studio 2005 as an optional part of the installation, or as a free download from Microsoft.com. To use one of the full versions of SQL Server, you must install and configure an ASP.NET application services database, and configure the SQL personalization provider to connect to that database. For more information, see Creating and Configuring the Application Services Database for SQL Server. You can also create and configure a custom provider to use with other, non-SQL databases or storage solutions. For more information and a code example, see Implementing a Membership Provider.

Creating a Personalizable User Control that Contains Reference Type Variables

In this part of the walkthrough, you create a user control that allows you to enter a friendly name and URL for a Web site. When you click the Save button, the friendly name is added to a BulletedList control. These values are stored by the Web Parts personalization feature on a per-user basis. Every time the user accesses the control coming from another page or in a new session, the system displays the saved BulletedList values.

NoteNote

You do not need to enable Web Parts personalization; it is enabled by default. For more information about personalization, see Web Parts Personalization Overview.

To create the personalizable user control

  1. In a text editor, create a new file to contain a user control, name it UrlList.ascx, and save it in your Web directory. The code that implements the control's behavior is contained in a separate file, as shown in step 4.

  2. In the new file, add a control directive in the programming language you are using, as shown in the following example.

    <%@ Control Language="VB" AutoEventWireup="false"  
    CodeFile="UrlList.ascx.vb" Inherits="WebParts_UrlList" %>
    
    <%@ Control Language="C#" AutoEventWireup="true" 
    CodeFile="UrlList.ascx.cs" Inherits="WebParts_UrlList" %>
    
  3. Below the directive, add the following elements to represent the control's user interface (UI).

    <table>
    
    <tr>
        <td> 
            <asp:Label ID="NameLabelID" Text="Name: " runat="server" />
        </td>
        <td>
            <asp:TextBox ID="NameTextBoxID" runat="server" />
        </td>
    </tr>
    
    <tr>
        <td>
            <asp:Label ID="UrlLabelID1" Text="Url:  " runat="server" />
        </td>
        <td>
            <asp:TextBox ID="UrlTextBoxID" runat="server" />
        </td>
    </tr>
    
    <tr>
        <td>
            <asp:Button ID="SaveID" runat="server" 
                OnClick="SaveButton_Click" Text="Save" />
        </td>
        <td>
            <asp:Button ID="ResetID" runat="server" 
                OnClick="ResetButton_Click" Text="Reset" />
        </td>
    </tr>
    
    </table>
    
    <div>
         // This is the complex variable whose status 
         // must be preserved.
        <asp:BulletedList 
            ID="BulletedListID" runat="server"
            DisplayMode="HyperLink"  Target="_blank" />
    
    </div>
    
  4. Create another new file to act as the companion file, and add the following code to it. The following example represents the companion file (UrlList.ascx.vb or UrlList.ascx.cs, depending on which language you are using) that implements the behavior of the preceding UrlList.ascx control. This code is divided in two parts. One part contains the code that handles the user input and the page load event; the other contains the code that implements the IPersonalizable interface. This allows the preservation of the BulletedList values -- that is, the personalization of the user control. This example implements the Load, Save, and IsDirty interface members.

    ' UrlList.ascx.vb
    Imports System
    Imports System.Collections
    Imports System.Web
    Imports System.Web.UI
    Imports System.Web.UI.WebControls
    Imports System.Web.UI.WebControls.WebParts
    
    Partial Class WebParts_VB_UrlList
        Inherits System.Web.UI.UserControl
        Implements IPersonalizable
    
        Private _userUrls As ArrayList
        Private _listDirty As Boolean
    
        ' This code implements the IPersonalizable members.
    
        Public Overridable ReadOnly Property IsDirty() As Boolean _
        Implements System.Web.UI.WebControls.WebParts.IPersonalizable.IsDirty
    
            Get
                Return _listDirty
            End Get
        End Property
    
    
        Public Overridable Shadows Sub Load(ByVal state As PersonalizationDictionary) _
        Implements System.Web.UI.WebControls.WebParts.IPersonalizable.Load
    
    
            If Not (state Is Nothing) Then
    
                Dim userUrlsEntry As PersonalizationEntry = state("userUrls")
                If Not (userUrlsEntry Is Nothing) Then
                    _userUrls = CType(userUrlsEntry.Value, ArrayList)
                End If
            End If
    
        End Sub 'Load
    
        Public Overridable Sub Save(ByVal state As PersonalizationDictionary) _
          Implements System.Web.UI.WebControls.WebParts.IPersonalizable.Save
    
            If Not (_userUrls Is Nothing) AndAlso _userUrls.Count <> 0 Then
                state("userUrls") = New PersonalizationEntry(_userUrls, _
                PersonalizationScope.User)
            End If
    
        End Sub
    
        ' This code handles the user's input.
        Protected Sub SaveButton_Click(ByVal sender As Object, _
        ByVal e As EventArgs)
    
            Dim name As String = NameTextBoxID.Text.Trim()
            Dim url As String = UrlTextBoxID.Text.Trim()
    
            Dim p As New Pair(name, url)
    
            If _userUrls Is Nothing Then
                _userUrls = New ArrayList()
            End If
            _userUrls.Add(p)
    
            BulletedListID.Items.Add(New ListItem(CStr(p.First), _
            CStr(p.Second)))
    
            _listDirty = True
    
        End Sub 'SaveButton_Click
    
    
        Protected Sub ResetButton_Click(ByVal sender As Object, _
        ByVal e As EventArgs)
    
            _userUrls = New ArrayList()
    
             BulletedListID.Items.Clear()
    
            _listDirty = True
    
        End Sub 'ResetButton_Click 
    
    
        Protected Sub Page_Load(ByVal sender As Object, _
        ByVal e As EventArgs)
            If Not (_userUrls Is Nothing) Then
                BulletedListID.Items.Clear()
                Dim p As Pair
                For Each p In _userUrls
                    BulletedListID.Items.Add(New _
                    ListItem(CStr(p.First), CStr(p.Second)))
                Next p
            End If
    
        End Sub 'Page_Load
    
    End Class
    
    // UrlList.ascx.cs
    using System;
    using System.Collections;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    
    public partial class WebParts_UrlList : System.Web.UI.UserControl, 
        IPersonalizable
    {
        private ArrayList _userUrls;
        private bool _listDirty;
    
           // This code implements the IPersonalizable members.
    
        public new void Load(PersonalizationDictionary state)
        {
            if (state != null)
            {
    
                PersonalizationEntry userUrlsEntry = state["userUrls"];
                if (userUrlsEntry != null)
                {
                    _userUrls = (ArrayList)userUrlsEntry.Value;
                }
    
            }
        }
    
        public void Save(PersonalizationDictionary state)
        {
    
            if ((_userUrls != null) && (_userUrls.Count != 0))
            {
                state["userUrls"] =
                    new PersonalizationEntry(_userUrls, PersonalizationScope.User);
            }
    
        }
    
        public virtual bool IsDirty
        {
            get
            {
                return _listDirty;
            }
        }
    
           // This code handles the user's input.
    
        protected void Page_Load(object sender, EventArgs e)
        {
            if (_userUrls != null)
            {
                BulletedListID.Items.Clear();
                foreach (Pair p in _userUrls)
                {
                    BulletedListID.Items.Add(
                        new ListItem((string)p.First, (string)p.Second));
                }
            }
    
        }
    
        protected void SaveButton_Click(object sender, EventArgs e)
        {
            string name = NameTextBoxID.Text.Trim();
            string url = UrlTextBoxID.Text.Trim();
    
            Pair p = new Pair(name, url);
    
            if (_userUrls == null)
            {
                _userUrls = new ArrayList();
            }
            _userUrls.Add(p);
    
            BulletedListID.Items.Add(
                    new ListItem((string)p.First, (string)p.Second));
    
            _listDirty = true;
        }
    
    
        protected void ResetButton_Click(object sender, EventArgs e)
        {
    
            _userUrls = new ArrayList();
    
             BulletedListID.Items.Clear();
    
            _listDirty = true;
    
        }
    
    } 
    
  5. Name the file UrlList.ascx.vb or UrlList.ascx.cs (depending on which language you are using), and save it in the root directory of your Web site.

    Security noteSecurity Note

    This control has a text box that accepts user input, which is a potential security threat. User input in a Web page can potentially contain malicious client script. By default, ASP.NET Web pages validate user input to ensure that the input does not contain HTML elements or script. As long as this validation is enabled, you do not need to explicitly check for script or HTML elements in user input. For more information, see Script Exploits Overview.

Referencing the User Control as a Web Parts Control

Now that you have created a user control with personalizable properties, you can create a Web Forms page to host the user control as a Web Parts control.

NoteNote

You must host the control as a Web Parts control for personalization to work.

To reference the user control as a Web Parts control

  1. In the text editor, create a new file. Add a page declaration to the beginning of the file, as shown in the following example.

    <%@ page language="VB" %>
    
    <%@ page language="C#" %>
    
  2. Beneath the page declaration, add a declaration to reference the user control you created previously, as shown in the following example.

    <%@ Register tagprefix="UserControl" tagname="UrlList" 
    src="UrlList.ascx" %>
    
    <%@ Register tagprefix="UserControl" tagname="UrlList" 
    src="UrlList.ascx" %>
    
  3. Beneath the control reference, add the following basic page structure to host the user control as a Web Parts control.

    NoteNote

    For the user control to work as a Web Parts control, the page must contain an <asp:webpartmanager> element, and the user control must be contained within an <asp:webpartzone> element and a <zonetemplate> element in succession, as the following example shows.

    <html>
    <head runat="server">
        <title>Personalizable User Control</title>
    </head>
    <body>
        <form id="form1" runat="server">
          <asp:WebPartManager ID="mgr" runat="server" />
            <div>
              <asp:WebPartZone ID="WebPartZone1" runat="server">
                <ZoneTemplate>
                  < UserControl:UrlList ID="AccUserID" runat="server"
                    title="URL List WebPart" />  
                </ZoneTemplate>
              </asp:WebPartZone>
            </div>    
        </form>
    </body>
    </html>
    
  4. Name the file UrlList.aspx and save it in the same directory as the user control.

    You have now created a personalizable user control, and referenced it as a Web Parts control in a Web Forms page.

The final step is to test your user control.

To test the personalizable user control

  1. Load the UrlList.aspx page in a browser.

  2. Enter values in the Name and URL fields, and click the Save Form Values button.

  3. Close the browser.

  4. Load the page again in a browser.

    The values you entered previously should appear in the form. These are the values that were saved in the BulletedList control, and were restored from the database when you reloaded the page in the browser.

  5. Enter new values in the form, but do not click the button to save them. Close the browser.

  6. Load the page again in a browser. The original values you entered and saved in the personalized properties should be the ones that reappear in the form.

Next Steps

This walkthrough has demonstrated the basic tasks involved in creating a user control with personalizable properties. You created a control that allows you to save user-specific settings for the specific control and page, and display those saved settings when the user revisits the page in a new browser session. Suggestions for further exploration include:

See Also

Concepts

ASP.NET Web Parts Overview
Web Parts Personalization Overview