Creating Personalizable Web Parts


Users can edit Web Part properties at runtime. For example, you might want to create a Web Part that displays database data. You can enable users to modify the SQL select query that returns the database records.

The Web Part Framework automatically saves the value of any property marked with the Personalizable attribute. By default, a personalizable property is scoped to a particular user. In other words, each user can personalize a property in different ways. However, you also have the option of creating a personalizable property that can be modified only in Shared user scope.

The Web Part in Listing 29.6, the DataPart Web Part, enables you to display records from any database table. The Web Part includes one personalizable property that has User scopethe SelectCommand propertyand one property that has Shared scopethe ConnectionString property.

Listing 29.6. DataPart.ascx

[View full width]

<%@ Control Language="VB" ClassName="DataPart" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <script runat="server">     Private _connectionString As String =  String.Empty     Private _selectCommand As String =  String.Empty     <Personalizable(PersonalizationScope.Shared)> _     <WebBrowsable> _     Public Property ConnectionString() As String         Get                  Return _connectionString         End Get         Set (ByVal Value As String)                  _connectionString = value         End Set     End Property     <Personalizable> _     <WebBrowsable> _     Public Property SelectCommand() As String         Get                  Return _selectCommand         End Get         Set (ByVal Value As String)                  _selectCommand = value         End Set     End Property     Private  Sub Page_PreRender()         If _connectionString <> String.Empty And_selectCommand <> String.Empty Then             Try                 Dim dad As SqlDataAdapter =  New SqlDataAdapter(_selectCommand ,_connectionString)                 Dim dst As DataSet =  New DataSet()                 dad.Fill(dst)                 grdData.DataSource = dst                 grdData.DataBind()             Catch e As Exception                 lblError.Text = e.Message             End Try         End If     End Sub </script> <asp:Label          EnableViewState="false"     Runat="server" /> <asp:GridView          Runat="server" /> 

Notice that the first property in Listing 29.6, the ConnectionString property, is decorated with a Personalizable attribute that includes a PersonalizationScope.Shared parameter. This property can be edited only by an administrator who can enter Shared personalization mode.

The second property, SelectCommand, is also decorated with the Personalizable attribute. This property can be modified in either Shared or User personalization mode. Modifying the property in Shared personalization mode provides the property with a default value for everyone. (Individual users can override the default value.)

You can experiment with the DataPart Web Part with the page in Listing 29.7. This page includes the Personalization Manager so you can toggle between User and Shared scope.

Listing 29.7. ShowDataPart.aspx

<%@ Page Language="VB" %> <%@ Register TagPrefix="user" TagName="DataPart" src="/books/3/444/1/html/2/~/DataPart.ascx" %> <%@ Register TagPrefix="user" TagName="PersonalizationManager"     src="/books/3/444/1/html/2/~/PersonalizationManager.ascx" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> <script runat="server">     Protected  Sub Menu1_MenuItemClick(ByVal sender As Object, ByVal e As MenuEventArgs)         WebPartManager1.DisplayMode = WebPartManager1.DisplayModes(e.Item.Text)     End Sub </script> <html xmlns="http://www.w3.org/1999/xhtml" > <head  runat="server">     <style type="text/css">         .personalizationManager         {             border:dotted 2px orange;             padding:5px;             background-color:White;             font:12px Arial, Sans-Serif;         }         .personalizationManager span         {             padding-right:10px;             margin-right:10px;             border-right:solid 1px black;         }         .personalizationManager a         {             text-decoration:none;         }         .column         {             float:left;             width:30%;             height:200px;             margin-right:10px;             border:solid 1px black;             background-color: white;         }         .menu         {             margin:5px 0px;         }         html         {             background-color:#eeeeee;         }     </style>     <title>Show Data Part</title> </head> <body>     <form  runat="server">     <user:PersonalizationManager                  Runat="Server" />     <asp:WebPartManager                  Runat="server" />         <asp:Menu                          OnMenuItemClick="Menu1_MenuItemClick"             Orientation="Horizontal"             Css             Runat="server">             <Items>             <asp:MenuItem Text="Browse" />             <asp:MenuItem Text="Design" />             <asp:MenuItem Text="Edit" />             </Items>         </asp:Menu>         <asp:WebPartZone                          Css             Runat="server">             <ZoneTemplate>             <user:DataPart                                  Title="Data Part"                 Description="Displays database records"                 Runat="server" />             </ZoneTemplate>         </asp:WebPartZone>         <asp:WebPartZone                          Css             Runat="server" />         <asp:EditorZone                          Css             Runat="server">             <ZoneTemplate>             <asp:PropertyGridEditorPart                                  Runat="server" />             </ZoneTemplate>         </asp:EditorZone>     </form> </body> </html> 

After you open the page in Listing 29.7, you can edit the properties of the DataPart Web Part by clicking the Edit link. The PropertyGridEditorPart control automatically renders a property sheet, which enables you to modify the values of the ConnectionString and SelectCommand properties (see Figure 29.3).

Figure 29.3. Editing the ConnectionString and SelectCommmand properties.


The ConnectionString property appears only when you enter Shared personalization scope by clicking the Toggle Scope link in the Personalization Manager control's information bar. The SelectCommand property, on the other hand, can be altered in either Shared or User personalization mode.

CD Note

The CD includes a sample database in the App_Data folder named MyDatabase.mdf, which includes a Movie database table. You can connect to this database with the following connection string:

Server=.\SQLExpress;Trusted_Connection=true; AttachDbFileName=|DataDirectory|MyDatabase.mdf;User Instance=true


And execute the following SQL SELECT statement:

SELECT * FROM Movies



Working with Complex Personalizable Properties

In the previous section, you learned how to use the Personalizable attribute with properties that represent simple types such as Strings and Integers. You also can use the Web Part Framework to automatically persist the values of properties that represent more complex types such as ArrayLists or custom classes.

Behind the scenes, the Web Part Framework uses the ObjectStateFormatter class to serialize and deserialize the values of Web Part properties. This is a powerful class. It can serialize the state of any class that can be represented statically.

Note

You should not use personalization with a property that returns an instance of a class defined in the App_Code folder. If you make changes to the App_Code folder, the contents of the folder are automatically recompiled into a new assembly. Because the assembly name changes with each recompilation, the Web Part Framework cannot automatically serialize and deserialize classes defined in the assembly.


However, one important limitation of the Web Part Framework relates to complex properties. The Web Part Framework can detect changes to simple properties automatically, but the framework cannot detect changes made to more complex properties automatically. In general, the Web Part Framework can detect changes to immutable properties, but not changes made to mutable properties.

Note

A mutable type is a type that has properties or fields that can change after it is instantiated. Most reference types are mutable. Most value types are immutable.


For example, if you attempt to use the Personalizable attribute with a property that returns an ArrayList, you don't get an exception, but the state of the ArrayList is not saved. The Web Part Framework fails to save the state of the property because the Web Part Framework cannot detect when the ArrayList has changed.

There are two ways around this limitation. The next section in this chapter discusses how you can take advantage of the IPersonalizable interface to take a more hands-on approach to personalization state management. When you implement the IPersonalizable interface, you can indicate exactly when you want the Web Part Framework to save changes to a Web Part's properties.

This section, however, explores a simpler option. The WebPart class includes a method named the SetPersonalizationDirty() method. There is both a shared (static) and an instance version of this method, so you can use it either when working with a Web Part created from a User Control or when working with a "True" Web Part derived from the base WebPart class.

For example, the Web Part in Listing 29.8the FirstTaskListPart Web Partenables you to create and save a task list (see Figure 29.4). The list of tasks is represented by an ArrayList.

Figure 29.4. A personalizable task list.


CD Note

You can view the FirstTaskListPart by opening the ShowFirstTaskListPart.aspx page included on the CD that accompanies this book.


Listing 29.8. FirstTaskListPart.ascx

<%@ Control Language="VB" ClassName="FirstTaskListPart" %> <script runat="server">     Private _tasks As ArrayList = Nothing     <Personalizable()> _     Public Property Tasks() As ArrayList         Get             Return _tasks         End Get         Set(ByVal Value As ArrayList)             _tasks = value         End Set     End Property     Private Sub Page_PreRender()         grdTasks.DataSource = _tasks         grdTasks.DataBind()     End Sub     Protected Sub btnAdd_Click(ByVal sender As Object, ByVal e As EventArgs)         If _tasks Is Nothing Then             _tasks = New ArrayList()         End If         _tasks.Add(txtNewTask.Text)         WebPart.SetPersonalizationDirty(Me)     End Sub </script> <asp:GridView          Runat="server" /> <hr> <b>New Task:</b> <asp:TextBox          Runat="server" /> <asp:Button          Text="Add"     OnClick="btnAdd_Click"     Runat="server" /> 

Notice that the WebPart.SetPersonalizationDirty() method is called after a new item is added to the ArrayList. If you neglected to call this method, then you would never be able to add more than a single item to the Task List.

Also, it is important to notice that the _tasks variable used to represent the tasks is initially set to Nothing (null). The variable is initialized like this:

Private _tasks As ArrayList = Nothing 


It is important that you initialize a personalizable property that represents a reference type with the value Nothing. The very first value assigned to a personalizable property is considered the default value. The Web Part Framework compares the current value against the default value and if there are no changes, the framework does not update the saved personalization data.

Imagine that you had initialized the property like this:

Private _tasks As ArrayList = New ArrayList() 


In that case, because an ArrayList is a reference type, the Web Part Framework would never detect a change in the property even when new items have been added to the ArrayList. (This is true even when you use the WebPart.SetPersonalizationDirty() method to warn the framework that there have been changes.)

Using the IPersonalizable Interface

In most cases, using the Personalizable attribute to mark the Web Part properties that you want to save automatically works fine. However, the Personalizable attribute does have some limitations:

  • A Personalizable property must be public.

  • A Personalizable property must have both a public get and set accessor.

  • A Personalizable property cannot have an indexer or parameter.

  • A Personalizable property is ignored in a nested control.

If you encounter one of these limitations, then you have no choice but to implement the IPersonalizable interface. When you implement this interface, you are responsible for selecting the data that you want to save.

The IPersonalizable interface includes one property and two methods that you must implement:

  • IsDirty Return the value TRue from this property when the Web Part Framework calls the Save() method.

  • Load() This method loads personalization state information.

  • Save() This method saves personalization state information.

For example, Listing 29.9 contains a task list Web Part that implements the IPersonalizable interface named SecondTaskListPart.ascx.

Listing 29.9. SecondTaskListPart.ascx

<%@ Control Language="VB" ClassName="SecondTaskListPart" %> <%@ Implements Interface="System.Web.UI.WebControls.WebParts.IPersonalizable" %> <script runat="server">     Private _tasks As ArrayList = New ArrayList()     Private _isDirty As Boolean = False     Public Property Tasks() As ArrayList         Get             Return _tasks         End Get         Set(ByVal Value As ArrayList)             _tasks = value         End Set     End Property     Public ReadOnly Property IsDirty() As Boolean Implements IPersonalizable.IsDirty         Get             Return _isDirty         End Get     End Property     Public Sub Save(ByVal state As PersonalizationDictionary) Implements IPersonalizable.Save         Dim wpm As WebPartManager = WebPartManager.GetCurrentWebPartManager(Page)         state.Add("tasks", New PersonalizationEntry(_tasks, wpm.Personalization.Scope))     End Sub     Public Sub Load(ByVal state As PersonalizationDictionary) Implements IPersonalizable.Load         _tasks = CType(state("tasks").Value, ArrayList)     End Sub     Private Sub Page_PreRender()         grdTasks.DataSource = _tasks         grdTasks.DataBind()     End Sub     Protected Sub btnAdd_Click(ByVal sender As Object, ByVal e As EventArgs)         _tasks.Add(txtNewTask.Text)         _isDirty = True     End Sub </script> <asp:GridView          Runat="server" /> <hr> <b>New Task:</b> <asp:TextBox          Runat="server" /> <asp:Button          Text="Add"     OnClick="btnAdd_Click"     Runat="server" /> 

CD Note

You can view the SecondTaskListPart Web Part with the ShowSecondTaskList.aspx page on the CD that accompanies this book.


Notice that the Web Part in Listing 29.9 includes an <%@ Implements %> directive and that the Web Part implements the IPersonalizable interface.

The body of the Web Part contains a GridView control, a TextBox control, and a Button control. When a user enters a new task description in the TextBox and clicks the button, the btnAdd_Click() method executes. This method adds the new task to the ArrayList and marks the Web Part as dirty.

Each time the Web Part is loaded in the page, the Load() method is called and the task list is returned from the underlying personalization data store. Whenever the Web Part is marked as dirtyafter a user enters a new taskthe Save() method is called. The Web Part framework calls this method before saving the personalization data.

Notice that both the Load() and Save() methods use a PersonalizationDictionary, which contains instances of the PersonalizationEntry class. Each PersonalizationEntry represents the information being saved and the personalization scope associated with the information being saved (User or Shared). In the Save() method, the current WebPartManager control is used to determine the page's current personalization scope.




ASP. NET 2.0 Unleashed
ASP.NET 2.0 Unleashed
ISBN: 0672328232
EAN: 2147483647
Year: 2006
Pages: 276

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net