Recipe 10.7. User-Personalized Themes


Problem

You want to provide to users of your application the ability to choose how the application looks.

Solution

Use the Profile features along with the Theme features in ASP.NET 2.0. Implement the profile features described in Recipe 10.1, modify web.config to add a property to store the selected theme in the profile, and set the selected theme in the Page_PreInit event handler of the pages in your application.

Modify web.config as follows:

  • Add an <add> element to the <properties> element to include the name of the user's selected theme in the profile.

In the code-behind class for the pages in your application, use the .NET language of your choice as follows:

  • Implement the event handler for the Page_PreInit event to set the theme for the page from the user's profile.

The solution we have implemented to demonstrate the solution is shown in Examples 10-19, 10-20, 10-21 through 10-22. Example 10-19 shows the modifications made to web.config. Example 10-20 shows the .aspx file used to enter the user profile data, and Examples 10-21 (VB) and 10-22 (C#) show the code-behind classes for the page used to enter the user profile data.

Discussion

In some cases, you'll want to allow the user to choose the look and feel she wants for your application. The combination of the profile and theme features in ASP.NET 2.0 provides the ability to implement this functionality in your application with very little code.

For our example, we built on the solution provided in Recipe 10.1, adding the ability to enter and store the user-selected theme. This requires adding a property to the profile defined in web.config to store the name of the selected theme.

 <?xml version="1.0"?> <configuration>   <system.web> … <profile enabled="true" defaultProvider="AspNetSqlProfileProvider" automaticSaveEnabled="false" >   <providers>     <remove name="AspNetSqlProfileProvider" /> <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="sqlConnectionString" applicationName="/AnonymousVB" />   </providers>   <properties>     <add name="FirstName" type="System.String" serializeAs="String" allowAnonymous="true" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" /> … <add name="Theme" type="System.String" serializeAs="String" allowAnonymous="true" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" /> … </properties>  </profile>   </system.web> </configuration> 

To provide the ability for the user to select the desired theme, we have added a dropdown to the .aspx file that is populated in the code-behind with a list of the folders within the App_Themes folder. The names of these folders are the available themes in the application.

 

get list of files in the images directory (just for example here) themes = Directory.GetDirectories(Server.MapPath("App_Themes")) 'for display purposes, remove the path to the theme For index = 0 To themes.Length - 1 themes(index) = New FileInfo(themes(index)).Name Next index 'bind the list of themes to the dropdown on the form ddThemes.DataSource = themes ddThemes.DataBind()

// get list of files in the images directory (just for example here) themes = Directory.GetDirectories(Server.MapPath("App_Themes")); // for display purposes, remove the path to the theme for (int index = 0; index < themes.Length; index++) { themes[index] = new FileInfo(themes[index]).Name; } // bind the list of themes to the dropdown on the form ddThemes.DataSource = themes; ddThemes.DataBind();

To apply the user's selected theme to a page at runtime, the Theme property of the Page object must be set in the Page_PreInit event handler. This is required because the theme information is loaded immediately after the Page_PreInit event. Any attempt to set the Theme property after the Page_PreInit event occurs will result in an InvalidOperationException being thrown.

Since it is possible for no theme to be set in the user's profile, you need to verify that data is available and set a default value if the data is not available:

 

If (Profile.Theme.Length = 0) Then 'user has not selected a theme so set the default Page.Theme = DEFAULT_THEME Else 'user has selected a theme so use it Page.Theme = Profile.Theme End If

if (Profile.Theme.Length == 0) { // user has not selected a theme so set the default Page.Theme = DEFAULT_THEME; } else

{ // user has selected a theme so use it Page.Theme = Profile.Theme; }

In our application, we provide the ability for the user to select a theme and apply it to the page. This presents a bit of a problem since, when the Page_PreInit event fires, the server controls on the page have not been initialized. Therefore, we cannot obtain the selected theme from the drop-down control as we would in most any other circumstance. This requires us to fall back on the approach used in classic ASP for getting the form data posted to the server. For those of you who do not remember those days, you must obtain the value from the Page.Request.Form collection.

Generally, all that would normally be required to get the value is to use the ID of the drop-down control as the key for the Form collection:

 'THIS WILL NOT WORK WHEN MASTER PAGES ARE USED Page.Theme = Page.Request.Form("ddThemes").ToString() 

In our application, we are using a master page, which complicates directly accessing the data in the Form collection. To guarantee unique names for controls on the rendered page, ASP.NET sets the name of the control to the unique name property of the server control. The name is a combination of the server control's ID and the content control's ID on the form, such as ctl00$PageBody$ddThemes. At first glance, it would appear you could use the UniqueID property of the themes drop-down control to access the Form data. Unfortunately, the server controls are not initialized, so the UniqueID property is unavailable.

To work around the problem, we write the value of the UniqueID property of the themes drop-down control to the rendered page in a hidden control when the form is initialized:

 

Private Const DD_THEMES_UNIQUE_ID As String = "ddThemeUniqueID" … ClientScript.RegisterHiddenField(DD_THEMES_UNIQUE_ID, _ ddThemes.UniqueID)

private const String DD_THEMES_UNIQUE_ID = "ddThemeUniqueID"; … ClientScript.RegisterHiddenField(DD_THEMES_UNIQUE_ID, ddThemes.UniqueID);

Because we are explicitly outputting the hidden control with a name of our choosing, we can get the stored value and use it to access the selected value for the themes drop-down control:

 

ddThemesUniqueID = Page.Request.Form(DD_THEMES_UNIQUE_ID).ToString() Page.Theme = Page.Request.Form(ddThemesUniqueID).ToString()

ddThemesUniqueID = Page.Request.Form[DD_THEMES_UNIQUE_ID].ToString(); Page.Theme = Page.Request.Form[ddThemesUniqueID].ToString();

The workaround we described above is only necessary when a form is used to input the user's theme selection and to display the same form using the selected theme. When applying the theme to other pages, this workaround is unnecessary.

To simplify the pages in your application, do the following:

  1. Create a base class that inherits from System.Web.UI.Page.

  2. Add a Page_PreInit event handler to set the Page.Theme property from the theme in the user's profile.

  3. Change the pages in your application to inherit from this new base class instead of the normal System.Web.UI.Page.

Using this approach allows you to implement the setting of the theme in one place for all your application pages.


See Also

Recipe 10.1

Example 10-19. Modifications to web.config to store theme name in the profile

 <?xml version="1.0"?> <configuration> <system.web> … <profile enabled="true" defaultProvider="AspNetSqlProfileProvider" automaticSaveEnabled="false" > <providers> <remove name="AspNetSqlProfileProvider" /> <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="sqlConnectionString" applicationName="/AnonymousVB" /> </providers> <properties> <add name="FirstName" type="System.String" serializeAs="String" allowAnonymous="true" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" /> <add name="LastName" type="System.String" serializeAs="String" allowAnonymous="true" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" /> <add name="Theme" type="System.String" serializeAs="String" allowAnonymous="true" provider="AspNetSqlProfileProvider" defaultValue="" readOnly="false" /> …   </properties>    </profile> </system.web> </configuration> 

Example 10-20. Update user profile data (.aspx)

 <%@ Page Language="VB" MasterPageFile="~/ASPNetCookbookVB.master" AutoEventWireup="false" CodeFile="CH10PersonalizedThemesVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH10PersonalizedThemesVB" Title="Personalized Themes" %> <asp:Content  runat="server" ContentPlaceHolder> <div align="center" >   Personalized Themes (VB) </div> <div align="center" >   <br />   <asp:Label  runat="server" /> </div> <table width="60%" align="center" border="0" cellpadding="2">   <tr> <td align="right" >FirstName: </td> <td><asp:TextBox  runat="server" /></td>   </tr>   <tr> <td align="right" >LastName: </td> <td><asp:TextBox  runat="server" /></td>   </tr>   <tr> <td align="right" >Theme: </td> <td><asp:DropDownList  runat="server" /></td>   </tr>   <tr> <td colspan="2" align="center">   <br />   <asp:Button  runat="server"  Text="Update"  OnClick="btnUpdate_Click" />   </td>   </tr>    </table> </asp:Content> 

Example 10-21. Update user profile data code-behind (.vb)

 Option Explicit On Option Strict On Imports System Imports System.IO Namespace ASPNetCookbook.VBExamples ''' <summary> ''' This class provides the code behind for ''' CH10PersonalizedThemesVB.aspx ''' </summary> Partial Class CH10PersonalizedThemesVB   Inherits System.Web.UI.Page   'the following constant defines the name of the default theme   Private Const DEFAULT_THEME As String = "Blue"   'the following constant defines the name of the hidden field placed   'in the form with the unique ID for the theme dropdown control   Private Const DD_THEMES_UNIQUE_ID As String = "ddThemeUniqueID"   '''****************************************************************   ''' <summary>   ''' This routine provides the event handler for the page preinit event.   ''' It is responsible for initializing the page theme.   ''' </summary>   '''   ''' <param name="sender">Set to the sender of the event</param>   ''' <param name="e">Set to the event arguments</param>   Protected Sub Page_PreInit(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.PreInit Dim ddThemesUniqueID As String 'get the selected theme If (Page.IsPostBack) Then   'page is being posted back so new theme is available in the form data   'NOTE: The theme must be extracted from the form data because the   '    server controls have not been initialized with the ViewState   '    data yet and the theme must be set in the Page PreInit event.   '   '    Since the names of the server controls and thus the keys in the   '    Form name/value collection are set to unique identifiers, the   '      form variables cannot be accessed using the ID values we   '      assigned. Because of this we have output a hidden HTML input   '      to the form with the unique ID we need to access the Form   '      variable.   ddThemesUniqueID = Page.Request.Form(DD_THEMES_UNIQUE_ID).ToString()   Page.Theme = Page.Request.Form(ddThemesUniqueID).ToString() Else   'page is being initially displayed so use the theme setting from the   'profile if it exists  If (Profile.Theme.Length = 0) Then    'user has not selected a theme so set the default    Page.Theme = DEFAULT_THEME      Else    'user has selected a theme so use it    Page.Theme = Profile.Theme    End If End If   End Sub 'Page_PreInit   '''********************************************************************   ''' <summary>   ''' This routine provides the event handler for the page load event. It   ''' is responsible for initializing the controls on the page.   ''' </summary>   '''   ''' <param name="sender">Set to the sender of the event</param>   ''' <param name="e">Set to the event arguments</param>   Private Sub Page_Load(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles Me.Load If (Not Page.IsPostBack) Then   initializeForm( ) End If   End Sub 'Page_Load   '''********************************************************************   ''' <summary>   ''' This routine provides the event handler for the update button click   ''' event. It is responsible for updating the user profile from the data   ''' on the page.   ''' </summary>   '''   ''' <param name="sender">Set to the sender of the event</param>   ''' <param name="e">Set to the event arguments</param>   Protected Sub btnUpdate_Click(ByVal sender As Object, _ ByVal e As System.EventArgs) Profile.FirstName = txtFirstName.Text Profile.LastName = txtLastName.Text Profile.Theme = ddThemes.SelectedItem.Text Profile.Save( ) initializeForm( )   End Sub 'btnUpdate_Click   '''********************************************************************   ''' <summary>   ''' This routine updates the form with data from the user's profile   ''' </summary>   Private Sub initializeForm( ) Dim themes( ) As String Dim index As Integer txtFirstName.Text = Profile.FirstName txtLastName.Text = Profile.LastName 'get list of files in the images directory (just for example here) themes = Directory.GetDirectories(Server.MapPath("App_Themes")) 'for display purposes, remove the path to the theme For index = 0 To themes.Length - 1   themes(index) = New FileInfo(themes(index)).Name Next index 'bind the list of themes to the dropdown on the form ddThemes.DataSource = themes ddThemes.DataBind( ) 'select the user's selected theme 'NOTE: Page.Theme was initialized to the correct theme in Page_PreInit ddThemes.SelectedIndex = _   ddThemes.Items.IndexOf(ddThemes.Items.FindByValue(Page.Theme)) 'output the unique identifier for the themes dropdown list to a hidden 'field. The need for this is explained in the Page_PreInit event handler ClientScript.RegisterHiddenField(DD_THEMES_UNIQUE_ID, _  ddThemes.UniqueID)    End Sub 'initializeForm   End Class 'CH10PersonalizedThemesVB End Namespace 

Example 10-22. Update user profile data code-behind (.cs)

 using System; using System.Configuration; using System.IO; using System.Web.UI.WebControls; using System.Web.Profile; namespace ASPNetCookbook.CSExamples { /// <summary> /// This class provides the code behind for /// CH10PersonalizedThemesCS.aspx /// </summary> public partial class CH10PersonalizedThemesCS : System.Web.UI.Page {   // the following constant defines the name of the default theme   private const String DEFAULT_THEME = "Blue";   // the following constant defines the name of the hidden field placed   // in the form with the unique ID for the theme dropdown control   private const String DD_THEMES_UNIQUE_ID = "ddThemeUniqueID";   ///****************************************************************   /// <summary>   /// This routine provides the event handler for the page preinit event.   /// It is responsible for initializing the page theme.   /// </summary>   ///   /// <param name="sender">Set to the sender of the event</param>   /// <param name="e">Set to the event arguments</param>   protected void Page_PreInit(Object sender,  System.EventArgs e)   { String ddThemesUniqueID; // get the selected theme if (Page.IsPostBack) {   // page is being posted back so new theme is available in the form data   // NOTE: The theme must be extracted from the form data because the   //  server controls have not been initialized with the ViewState   //  data yet and the theme must be set in the Page PreInit event.   //   //  Since the names of the server controls and thus the keys in   //  the Form name/value collection are set to unique identifiers,   //  the form variables cannot be accessed using the ID values we   //  assigned. Because of this we have output a hidden HTML input   //  to the form with the unique ID we need to access the Form   //  variable.   ddThemesUniqueID = Page.Request.Form[DD_THEMES_UNIQUE_ID].ToString( );   Page.Theme = Page.Request.Form[ddThemesUniqueID].ToString( ); } else {   // page is being initially displayed so use the theme setting from the   // profile if it exists   if (Profile.Theme.Length == 0)   { // user has not selected a theme so set the default Page.Theme = DEFAULT_THEME;   }   else   { // user has selected a theme so use it Page.Theme = Profile.Theme;   } } } // Page_PreInit ///**************************************************************** /// <summary> /// This routine provides the event handler for the page load event. /// It is responsible for initializing the controls on the page. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void Page_Load(object sender, EventArgs e) { if (!Page.IsPostBack) {   initializeForm( ); } } // Page_Load ///**************************************************************** /// <summary> /// This routine provides the event handler for the update button click /// event. It is responsible for updating the user profile from the data /// on the page. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void btnUpdate_Click(Object sender, System.EventArgs e) { Profile.FirstName = txtFirstName.Text; Profile.LastName = txtLastName.Text; Profile.Theme = ddThemes.SelectedItem.Text; Profile.Save( ); initializeForm( ); } // btnUpdate_Click ///**************************************************************** /// <summary> /// This routine updates the form with data from the user's profile /// </summary> private void initializeForm( ) { String[] themes; txtFirstName.Text = Profile.FirstName; txtLastName.Text = Profile.LastName; // get list of files in the images directory (just for example here) themes = Directory.GetDirectories(Server.MapPath("App_Themes")); // for display purposes, remove the path to the theme for (int index = 0; index < themes.Length; index++) { themes[index] = new FileInfo(themes[index]).Name;   }   // bind the list of themes to the dropdown on the form   ddThemes.DataSource = themes;   ddThemes.DataBind( );   // select the user's selected theme   // NOTE: Page.Theme was initialized to the correct theme in Page_PreInit   ddThemes.SelectedIndex = ddThemes.Items.IndexOf(ddThemes.Items.FindByValue(Page.Theme));   // output the unique identifier for the themes dropdown list to a hidden   // field. The need for this is explained in the Page_PreInit event   // handler.   ClientScript.RegisterHiddenField(DD_THEMES_UNIQUE_ID,    ddThemes.UniqueID); } // initializeForm   } // CH10PersonalizedThemesCS } 



ASP. NET Cookbook
ASP.Net 2.0 Cookbook (Cookbooks (OReilly))
ISBN: 0596100647
EAN: 2147483647
Year: 2003
Pages: 202

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