Pages

Advertisement

Saturday, September 22, 2007

Consuming Membership and Profile Services via ASP.NET AJAX

 

ASP.NET 2.0 introduced various application services—such as Membership, Roles, and Profiles—that eliminate a lot of coding that was required to provide the same functionality. However, these services are part of ASP.NET's server-side framework, which could pose a challenge when you use ASP.NET AJAX to consume the services from client-side JavaScript code. Fortunately, ASP.NET AJAX provides an out-of-the-box solution to this problem. This article explains how to use this solution in C# with Visual Studio.

Sample Scenario

Suppose you are developing a new web site and want to implement forms authentication. The web site will have a user registration page, a login page, and one or more pages that you must secure. The user registration and login pages use ASP.NET AJAX for an enhanced user experience. Also, the site must capture details such as birth date and address at the time of registration. This information is to be stored in the Profile of the user.

To develop a web site that fulfills all the above requirements, begin by creating a new ASP.NET AJAX-enabled web site with C# (see Figure 1).


Figure 1. Creating a New ASP.NET AJAX-enabled Web Site

Configuring the Web Site

Before you begin coding, configure the web site for forms authentication as well as Membership and Profile services. Open a web.config file in your Visual Studio IDE and add the following markup inside the connectionStrings section:

<connectionStrings>
   <add name="connstr"
        connectionString="data source=.\sqlexpress;
        initial catalog=northwind;
        integrated security=true"
        providerName="System.Data.SqlClient"/>
</connectionStrings>

You specified a database connection string named connstr that points to a Northwind database. Make sure to change the connection string to match your development environment. I assume that your database is configured for application services using the aspnet_regsql.exe tool. You will use this connection string while configuring membership and profile providers.

Now, add the following markup inside the system.web section:


<system.web>
<authentication mode="Forms">
   <forms loginUrl="Login.aspx"></forms>
</authentication>
<authorization>
   <deny users="?"/>
</authorization>
<membership defaultProvider="p1">
   <providers>
      <add name="p1"
           connectionStringName="connstr"
           type="System.Web.Security.SqlMembershipProvider"
           requiresQuestionAndAnswer="false"/>
   </providers>
</membership>
<profile defaultProvider="p2">
   <providers>
      <add name="p2"
           connectionStringName="connstr"
           type="System.Web.Profile.SqlProfileProvider"/>
   </providers>
   <properties>
      <add name="FullName"/>
      <add name="DOB" type="System.DateTime"/>
      <group name="Address">
      <add name="Street"/>
      <add name="Country"/>
      <add name="PostalCode"/>
      </group>
   </properties>
</profile>
 

Review the above markup carefully, and you'll notice the following:


  • The authentication section sets the authentication mode to Forms. The forms tag sets the URL of the login page by using the loginUrl attribute.
  • The authorization section disables anonymous users by setting the users attribute of the deny tag to "?".
  • The membership section configures a membership provider named p1. (You can change this any name you choose.)
  • The connectionStringName attribute specifies the database that will be used for storing membership information.
  • The type attribute indicates the class that will act as the membership provider. You use the built-in SQL Membership provider called SqlMembershipProvider.
  • The requiresQuestionAndAnswer attribute indicates that you do not intend to accept a secret question and answer from the end user at the time of registration.
  • The profile section configures a profile provider named p2 and various profile properties. The significance of the connectionStringname and type attributes is same as for the membership section. Note, however, that this time the type is a SqlProfileProvider class. The properties section defines profile properties and groups.
  • You defined two simple properties called FullName and DOB and a property group called Address. The Address group further contains three properties: street, country, and postalcode. The DOB property is of type DateTime; therefore, its type attribute is set to System.DateTime.

Now that you have configured your web site for using forms authentication and membership services, it's time to expose Membership and Profile services to the client-side AJAX code. The web.config file will have a pre-defined section called webServices. By default, all its content is commented. You need to un-comment and modify it so that it looks as shown below:



<webServices>
   <authenticationService enabled="true"
   requireSSL="false"/>
   <profileService enabled="true"
   readAccessProperties="FullName,DOB,Address.Street,Address.Country,
                         Address.PostalCode"
   writeAccessProperties="FullName,DOB,Address.Street,Address.Country,
                          Address.PostalCode"/>
</webServices>

The authenticationService tag is used to expose forms authentication and membership services to AJAX code. The enabled attribute governs whether AJAX code can avail membership services. The requireSSL attribute indicates whether the authentication is happening over SSL. Similarly, the Profile service is exposed to AJAX code by using the profileService tag. The readAccessProperties and writeAccessProperties attributes of the profileService tag specify the profile properties that are readable and writable, respectively. Notice how the grouped properties are specified using the dot (.) notion. If you do not include a specific profile property in these attributes, it will not be accessible to the client code.

Applying forms authentication ensures that all the forms of the web site except the login page are secured. However, you want your registration page to be unsecured because new users will need to access it. Do this by adding a location section in the web.config file as shown below:



<location path="register.aspx">
   <system.web>
      <authorization>
         <allow users="*"/>
      </authorization>
   </system.web>
</location>

The path attribute of the location tag specifies a virtual path of a file or folder that is to be configured. It then allows access to all the users using the authorization section and allow tag.

This completes the web site configuration. Now, you will move on to develop the required web forms.


User Registration

First of all, you will create the user registration page. Add a new web form named Registration.aspx. Drag and drop a ScriptManager control from the toolbox (see Figure 2).



Figure 2. Drag and Drop a ScriptManager Control

Also, drag and drop an UpdatePanel and UpdateProgress control on the web form. The UpdatePanel control represents a part of the total web form that can be refreshed without causing a post back of the entire form. The UpdateProgress control is used to display a progress message while the UpdatePanel is being refreshed.

Drag and drop a Label control inside the UpdateProgress control and set its Text property to "Please wait...". Also, set its AssociatedUpdatePanelID property to the ID of the UpdatePanel control. The AssociatedUpdatePanelID property links the UpdateProgress with an UpdatePanel.

Add a table into the UpdatePanel and design it as shown in Figure 3.


Figure 3. Design for Table in the UpdatePanel

The first column of the table contains Label controls that act as prompts for the textboxes. The second column of the table contains TextBox controls. Each TextBox control is validated by using a RequiredFieldValidator control. The TextMode property of the password and confirm password textboxes is set to Password. Similarly, the TextMode property of the street address textbox is set to MultiLine.

There is a Button called "Check Availability" that the end user can use to check the availability of a user ID. The "Check Availability" button will make an AJAX call to the web form to decide whether the specified user ID is available for registration. Set the OnClientClick property of the "Check Availability" button to "return CheckAvailability();" (CheckAvailability() is a client-side JavaScript function that you will write later). This function will call a web method to decide whether the user ID is available for registration. Finally, the Register button will create the user in the system with the help of the Membership features. The Label at the bottom is used for displaying success or error messages.

Now, go in the code behind of the Register.aspx and add a static web method called CheckAvailability. The following is the complete code of the method:



[WebMethod]
public static bool CheckAvailability(string uid)
{
   MembershipUser user = Membership.GetUser(uid);
   if (user == null)
   {
      return true;
   }
   else
   {
      return false;
   }
}

You might be wondering why you added a web method inside a web form. Remember that you have a "Check Availability" button that is supposed to check whether the specified user ID is available for registration. You will be making an AJAX call to do that. ASP.NET AJAX allows you to call web methods defined in web forms via an object called PageMethods. Therefore, you marked the CheckAvailability() method with a [WebMethod] attribute. Note that you must refer to the System.Web.dll and import the System.Web.Services namespace to use the [WebMethod] attribute.

The CheckAvailability() method accepts a user ID and returns true if that ID is available for registration. Inside, it calls the GetUser() method of the Membership object. The GetUser() method returns an instance of the MembershipUser class that represents the specified user. If it returns null, it indicates that the specified user doesn't exist and accordingly true or false is returned to the caller.

When the user clicks the Register button, you need to add user details in the membership and profile tables. Use the Membership and Profile objects to do this BECAUSE ASP.NET AJAX doesn't allow you to create users from client-side code. The following code shows the Click event handler of the Register button:



protected void Button1_Click(object sender, EventArgs e)
{
   try
   {
      MembershipUser user   = Membership.CreateUser
      (TextBox2.Text, TextBox3.Text, TextBox5.Text);
      ProfileCommon pc      = Profile.GetProfile(user.UserName);
      pc.FullName           = TextBox1.Text;
      pc.DOB                = DateTime.Parse(TextBox6.Text);
      pc.Address.Street     = TextBox7.Text;
      pc.Address.Country    = TextBox8.Text;
      pc.Address.PostalCode = TextBox9.Text;
      pc.Save();
      lblMsg.Text           = "User created successfully!";
   }
   catch (Exception ex)
   {
      lblMsg.Text = ex.Message;
   }
}

You call the CreateUser() method of the Membership object to create the user and pass user ID, password, and email. The CreateUser() method returns an instance of MembershipUser representing the newly created user. At this point, the user is not authenticated, so you cannot set the user's profile directly via the Profile object. Instead, you call the GetProfile() method of the Profile object. The GetProfile() method returns an instance of the ProfileCommon class. Through this instance, you set various profile properties. Once all the profile properties are saved, the Save() method of the ProfileCommon class is called to save profile information to the underlying database. A success message is then displayed in a Label control. Any exceptions during the registration process are captured BY using try-catch blocks and an error message is displayed in a Label control.

Now, code the client-side CheckAvailability() function. Switch to the HTML source view of the Register.aspx and add a script block in the HEAD section of the page. Then, add the following functions in the script block:



function CheckAvailability()
{
   var uid=document.getElementById('TextBox2').value;
   if(uid=="")
   {
      alert('Please enter user ID!');
      return false;
   }
   PageMethods.CheckAvailability(uid,OnComplete);
   return false;
}
 
function OnComplete(result)
{
   var lblMsg=document.getElementById('lblMsg');
   if(result)
   {
      lblMsg.innerText="The ID is available!";
   }
   else
   {
      lblMsg.innerText="The ID is unavailable!";
   }
}

The CheckAvailability() function retrieves the user ID textbox using the getElementById() method of the HTML DOM, which accepts the ID of an element and returns a reference to it. The code checks whether the user ID is empty and, if so, displays an error message. It then calls the CheckAvailability() web method via the PageMethods object and passes the specified user ID to it. The PageMethods object is a built-in object provided by ASP.NET AJAX that allows you to call web methods defined in web forms. The second parameter of the CheckAvailability() call is nothing but the name of another JavaScript function (OnComplete in this example) that gets called after the web method call completes. You may find this mechanism a bit odd, but remember that ASP.NET AJAX communication is always asynchronous. The OnComplete() function receives the return value of the web method as a result parameter. It then simply displays a success or error message in a Label control. Note that the CheckAvailability() JavaScript function returns false so that there won't be any post back.

This completes your registration page. To test it, run the Register.aspx in the browser and try creating new users. Also, check how the "Check Availability" button works. Figure 4 shows a sample run of the web form.


(
Full Size Image)

Figure 4. Sample Run of the Web Form

Developing a Login Page

Now that users can register themselves with the web site, you need to provide a facility that enables them to log in and access various pages. To do so, add a new web form called Login.aspx to the web site. Remember that you have set the loginUrl attribute of the forms tag to Login.aspx. Drag and drop a ScriptManager control on it and design the login page as shown in Figure 5 by assembling various controls.


Figure 5. The Login Page Design

As you can see, the login page consists of textboxes for entering a user ID and password. The "Remember Me" checkbox allows you to preserve your logged-in status even after closing the browser window. The TextMode property of the password textbox is set to Password. Further, the OnClientClick property of the Login button is set to "return BeginAuthenticateUser();". BeginAuthenticateUser() is a JavaScript function that uses the ASP.NET AJAX authentication service to authenticate the user. The following is the BeginAuthenticateUser() function:



function BeginAuthenticateUser()
{
   var uid;
   var pwd;
   var isPersistent;
   uid=document.getElementById('TextBox1').value;
   pwd=document.getElementById('TextBox2').value;
   isPersistent=document.getElementById('CheckBox1').checked;
   Sys.Services.AuthenticationService.login
   (uid,pwd,isPersistent,null,null,
   EndAuthenticateUser,OnError,uid);
   return false;
}

The BeginAuthenticateUser() JavaScript function retrieves the user IDs and passwords entered in their respective textboxes. It also retrieves the status of the "Remember Me" checkbox. ASP.NET AJAX provides a built-n class called AuthenticationService that resides in the Sys.Services namespace. Remember that the Sys.Services namespace is defined by the client-side framework of ASP.NET AJAX. The AuthenticationService class offers two methods: login() and logout(). The code above used the login() method, which takes in all eight parameters. Their significance is listed below:

Parameter Significance


  1. A user ID

  2. A password

  3. A boolean value indicating whether an authentication cookie will be persistent

  4. The web page where the user should be redirect after a successful login

  5. Reserved for future use

  6. A callback function that will be called after a successful login (EndAuthenticateUser in this example)

  7. A callback function that will be called in case a login attempt fails (OnError in this example)

  8. A custom value that is passed to the callback functions

If the user is successfully authenticated, the EndAuthenticateUser function will be called. The following is the EndAuthenticateUser function:



function EndAuthenticateUser(result,userContext,methodName)
{
   if(result)
   {
      window.location='default.aspx';
   }
   else
   {
      alert("Unable to login! Please check user id and password!!");
   }
}

The EndAuthenticateUser() function takes three parameters: the result of the login operation, the user context that you passed earlier in the eighth parameter of the login() method, and the method name. Inside, it checks whether the result is true (in other words, the user is successfully authenticated) and, if so, it sets the location property of the windows object to default.aspx. This way, the user is redirected to the default page after a successful login attempt. If there is any error, an error message is displayed using the alert() function.

The OnError() function is called whenever an error occurs when calling the authentication service. This function is shown below:

function OnError(result,userContext,methodName)
{
alert(result.get_message());
}

The function simply displays an error message to the user. The result parameter received is actually an object and has a method called get_message() that returns a descriptive error message.

This completes the login page.

Implementing Logout Functionality

Add another web form called Default.aspx. This web form will allow users to logout and manage their profiles. Firstly, you will implement logout functionality. Drag and drop a ScriptManager control on the Default.aspx and design the web form as shown in Figure 6.



Figure 6. The Web Form Design

The web form consists of a couple of Label controls to display a welcome message to the user. The Logout button allows the user to delete the authentication cookie, thus logging him out. The OnClientClick property of the Login button is set to "return BeginLogOut();" (BeginLogout() is a JavaScript function that you will write later). The "Show My Profile" button toggles the profile panel. The OnClientClick property of the "Show My Profile" button is set to "return BeginProfileLoad();" (you will create the BeginProfileLoad() function later). The profile panel consists of a Panel control containing textboxes to display and show profile property values. It also contains the "Save Profile" button for saving changes made to the profile values. The OnClientClick property of the "Save Profile" button is set to "return BeginSaveProfile();" (the BeginSaveProfile() function will be coded later).

In the Page_Load event of the web form, you set the welcome label to the ID of the user. The following code shows how:

protected void Page_Load(object sender, EventArgs e)
{
Label4.Text = Membership.GetUser().UserName;
}

The code simply retrieves the user name of the current user and assigns it to the label. Note that the GetUser() method of the Membership object returns an object of type MembershipUser. The UserName property of the MembershipUser class is then called. You need to display the user name from the server-side code because ASP.NET AJAX doesn't provide any way to retrieve it via client-side code.

Now, switch to the HTML source view of Default.aspx and write the BeginLogOut() and EndLogOut() JavaScript functions as shown below:



function BeginLogOut()
{
   Sys.Services.AuthenticationService.logout
   (null,EndLogout,OnError,null);
   return false;
}
 
function EndLogout(result)
{
   //nothing here
}

The BeginLogOut() function again uses the AuthenticationService class. This time, it calls the logout() method of AuthenticationService. The logout() method takes the following four parameters:


As before, the BeginLogOut() function returns false so that there is no post back. The EndLogOut() function doesn't perform any action in this example.

Reading Profile Properties

Initially, the profile panel should be hidden from the end user. This is done in the pageLoad() JavaScript function. Note that the pageLoad() function is called by the ASP.NET AJAX framework when the page loads in the browser. You can think of it as a client-side counterpart of server-side Page_Load event. The pageLoad() function is shown below:

function pageLoad()
{
var panel3=document.getElementById('Panel3');
panel3.style.visibility="hidden";
}

The pageLoad() function simply sets the visibility property of the style object to hidden, thus hiding the profile panel.

When you click on the "Show My Profile" button, the profile panel needs to be displayed with the profile property values filled in. The BeginProfileLoad() function does this job:



function BeginProfileLoad()
{
   if(event.srcElement.value=="Show my profile")
   {
      var panel3=document.getElementById('Panel3');
      panel3.style.visibility="visible";
      event.srcElement.value="Hide my profile";
      Sys.Services.ProfileService.load
      (null,EndProfileLoad,OnProfileFailed, null);
   }
   else
   {
      var panel3=document.getElementById('Panel3');
      panel3.style.visibility="hidden";
      event.srcElement.value="Show my profile";
   }
   return false;
}

The BeginProfileLoad() function toggles visibility of the profile panel. If the profile panel is to be displayed, then you must populate various textboxes with profile values. The ASP.NET AJAX framework provides a class called ProfileService that allows you to work with profile properties. The load() method of the ProfileService class loads profile property values. The load() method takes four parameters, which do the following:

Parameter
Significance


  1. An array of property names that are to be loaded. If you have too many profile properties, then it makes sense to load the ones that you really want to use. This will improve the performance of your page.

  2. A callback function that will be called when the load operation is completed

  3. A callback function that will be called if the load operation fails

  4. Custom context information, if any

Once the profile is loaded the EndProfileLoad() function is called. This is the EndProfileLoad():



function EndProfileLoad(numProperties, userContext, methodName)
{
   document.getElementById('TextBox3').value =
      Sys.Services.ProfileService.properties.FullName;
   document.getElementById('TextBox4').value =
      Sys.Services.ProfileService.properties.DOB;
   document.getElementById('TextBox5').value =
      Sys.Services.ProfileService.properties.Address.Street;
   document.getElementById('TextBox6').value =
      Sys.Services.ProfileService.properties.Address.Country;
   document.getElementById('TextBox7').value =
      Sys.Services.ProfileService.properties.Address.PostalCode;
}

The EndProfileLoad() function receives three parameters: number of properties that are loaded, context information that is supplied to the load() function, and method name. It then populates the textboxes with the profile property values. The ProfileService class exposes profile properties via the properties collection. Remember that only the properties specified in the readAccessProperties attribute of the profileService tag of web.config are exposed. The properties and groups are accessed with the familiar dot (.) notion.

Modifying Profile Properties

When profile property values are displayed in various textboxes, the user can change them and click on the "Save Profile" button. Clicking on the "Save Profile" button calls the BeginSaveProfile() function, which is shown here:



function BeginSaveProfile()
{
   Sys.Services.ProfileService.properties.FullName=
      document.getElementById('TextBox3').value;
   Sys.Services.ProfileService.properties.DOB=
      document.getElementById('TextBox4').value;
   Sys.Services.ProfileService.properties.Address.Street=
      document.getElementById('TextBox5').value;
   Sys.Services.ProfileService.properties.Address.Country=
      document.getElementById('TextBox6').value;
   Sys.Services.ProfileService.properties.Address.PostalCode=
      document.getElementById('TextBox7').value;
   Sys.Services.ProfileService.save
   (null,EndSaveProfile,OnProfileFailed,null);
   return false;
}

The code again uses the ProfileService class and its properties collection to assign new values. Once all the profile properties are set, it calls the save() method of the ProfileService class. The save() method takes the same four parameters as the load() method (in other words, the array of properties to write, the callback function to be called after successful saving, the callback function to be called after unsuccessful save, and custom context information). The EndSaveProfile() function simply displays a message box to the user:

function EndSaveProfile(numProperties, userContext, methodName)
{
alert('Your profile is saved successfully!');
}

That's it! You just completed your AJAX-driven membership and profile pages. You now can log in to the web site and test the profile features. Figure 7 shows a sample run of the default.aspx.


Figure 7. A Sample Run of the Default.aspx

Consuming Membership and Profile Features from the Client Side

ASP.NET AJAX provides a handy way to consume membership and profile features. To consume these features, you need to enable them by using the authenticationService and profileService tags of web.config. Once enabled, you can use the AuthenticationService and ProfileService classes to consume them.

BACKLINK


Localization Made Easy with ASP.NET 2.0

 

In this article, I cover the basics of the globalization and localization support in ASP.NET 2.0. I have included a setup file that creates a Web site with samples for this article.

Globalization and Localization

Globalization is the process of designing your application so that it is possible later to have it translated and support multiple cultures. So, here you might not even actually make any translations but are making the application ready for it. Once you have globalized your application and now want to translate it into a specific culture, the translation process is called localization.

To globalize your application, the most important change to be done is that instead of hard-coding things such as display strings, move them to resource files. You then can set the control properties by reading the resource files. When you want to localize your application, have the resources translated to the languages you want to support. This will be covered in detail in this article.

The Difference Between Culture and Language

Culture consists of language plus the country/region. Culture is usually denoted by a combination of a two-letter lowercase code (called culture code) denoting the language and a two-letter uppercase code (called sub-culture code) denoting the country or region. For example, "de-DE" for German in Germany and "de-AT" for German in Austria (even though the language is the same, they are different cultures!).

Note: A neutral culture is a culture that is associated with a language but not with a country/region, for example, "de" for German.

Culture affects not only things like which language a particular string is shown but also things like the date format used, the decimal separator, currency symbol, and so on.

Culture and UICulture

In ASP.NET, you can set to two culture values: the Culture and UICulture properties. The Culture value determines things such as date, number, currency formatting, and so on. For example, it is the Culture setting that decides whether the date is in the dmy format or mdy format and what names to use for the month and for numeric values what the decimal separator is. The UICulture value determines which resources are loaded for the page. So, it controls things like which translated text to show. In most cases, you will set Culture and UICulture to the same culture.

Culture is set at the thread level by setting the CurrentCulture and CurrentUICulture properties of the thread. You either can set it directly or ASP.NET will set it for you.

In an ASP.NET application, culture can be set in one of these three ways:

  1. Set it declaratively: In the web.config file or in the @Page directive in the page, you can specify the culture as shown:
    web.config: <globalization uiCulture="es-MX" culture="en-US" />
    @Page Directive: <%@ Page  UICulture="es-MX" Culture="es-MX" %>

  2. From the client browser's language setting: Here, the user can specify their language of choice through the Languages option in their browser. To see a live example, change your languages option and view the GoogleMail login page. Notice how they change the display language accordingly.
    Note: For ASP.NET to use the browser language setting, you need to set the uiCulture and culture settings of web.config and/or the @Page directive to "auto."


    Figure 1a: Setting Languages options for Internet Explorer (IE)


    Figure 1b: Setting Languages option for Mozilla FireFox


Set it programmatically: In most cases, you will need to give an explicit way on your Web site for the user to select their language of choice. In such cases, you need to set the culture programmatically. Set these in the InitializeCulture method for the page.



Thread.CurrentThread.CurrentCulture =
   CultureInfo.CreateSpecificCulture(selectedLanguage);
Thread.CurrentThread.CurrentUICulture =
   new CultureInfo(selectedLanguage);

Note: If none of the above are set, ASP.NET will set the Culture and UICulture to that in the Regional Settings of the Web server.


Resource Files

To facilitate localization into different cultures, move strings you want to translate and localizable controls properties to a resource file. For example, if you want to change the string displayed in a Label and its ForeColor depending on the culture, you should move them to a resource file.

The resource file is an XML file and has a resx extension. Each resource in the resx file is in the form of a key-value pair. When you create resource files, start by creating a base .resx file. This is the default, or fallback resource file. For each culture that you want to support, create a copy of the base file and change the name so that it has the same basic file name, but with the culture name as part of it. For example, you might create the following files:


  • WebResources.resx: The base resource file. This is the default, or fallback resource file
  • WebResources.es.resx: A resource file for Spanish
  • WebResources.es-mx.resx: A resource file for Spanish (Mexico)
  • WebResources.de.resx: A resource file for German

At run time, ASP.NET uses the resource file that is the best match for the processing thread's CurrentUICulture property (I earlier described the ways in which this property gets set in ASP.NET). For example, if the current UI culture is Spanish (Mexico), resources from WebResources.es-mx.resx will be used. If it's Spanish (Costa Rica), ASP.NET will use WebResources.es.resx as its the best match. If the current UI culture is Swedish (Finland), which has no match in the above case, ASP.NET will use WebResources.resx as its the fallback resource file.

Local and Global Resource Files

Resource files are of two types: local resource files and global resource files.

Local resource files are specific to a single ASP.NET Web page, master page, or user control (.aspx, .master, or .ascx file). The resources in these files can be used only for that ASP.NET Web file. These resource files are put into the special ASP.NET App_LocalResources folder. A local resource file is named after the ASP.NET Web file; for example, the resource file for the Home.aspx Web page could be Home.aspx.resx, Home.aspx.es-mx.resx, and so on.

Note: Each sub-folder of the Web site can have its own App_LocalResources folder.

The resources in a global resource file can be read from any page or code in the Web site. These resource files are put into the special ASP.NET folder App_GlobalResources. This folder exists at the root of the ASP.NET application. Any .resx file that is in the App_GlobalResources folder has global scope.


Figure 2: Resource folders and resource files in a Web site as seen in Solution Explorer

Creating Resource Files

You can create a new resource file in any of the following ways:

Resource Generator tool (Resgen.exe): You can use this tool to help you generate the XML resouce file. To use this, place the key-value pairs in a text file; for example:

Label1.Text=Hello World!
Label1.ForeColor=Blue

You then can use the Resource Generator to generate an XML resource file from this text file. You then can copy this resx file into the global or local resource folder.


Using the Generate Local Resource option: You can use this option in Visual Studio 2005 to generate local resources for a Web page (.aspx, .master, or .ascx file). Open your page in design view and run this tool. It will generate the base (default) resx file for your page. The tool will move all localizable properties of controls on your page, such as the TextBox's Text property, Label's ForeColor property, and Page's Title property and so on, to the resource file. You then can delete those entries you do not wish to localize.


Manually: You can add an empty resource file by using the Add New Item option of Visual Studio. You can add resource entries to this file by opening them in Visual Studio's resource editor.


Setting Control Properties to Use Localized Resource Values

Once you have moved the control properties you want to localize to the resource file, you need to make changes in your page file or in the code-behind classes so that it picks up property values from the resource file. You might use resources to set the Text property of a Label control, instead of hard-coding the property to a specific string.

You can set the control properties by using localized values from resources in the following ways:

Implicit Localization

In this method, you don't set control properties one by one. Here, ASP.NET reads the resource file and sets all matching properties. For example, here is a Label control that uses implicit localization:

<asp:Label ID="MyLabel" meta:resourcekey="MyLabel" />

Here is a resource file with some properties set for this control:

MyLabel.Text=User Name
MyLabel.ForeColor=Blue

At runtime, ASP.NET takes the resourcekey and picks all properties set in the resource file (using the syntax resourcekey.PropertyName).

Implicit Localization can be used only to read values from local resource files.

Note: When you use the Generate Local Resource option to generate resources, Visual Studio automatically places the resourcekey tag in the aspx file.


Explicit Localization

Here, you explicitly set the control property to read a value from a local or global resource file. To set the property, you use a resource expression. The synatx is as follows.

To read from a global resource file:

<asp:Button ID="Button1" runat="server"
Text="<%$ Resources:MyGlobal1, Button1Caption %>" />

To read from a local resource file:

<asp:Button ID="Button1" runat="server"
Text="<%$ Resources: Button1Caption %>" />

Setting it programmatically

Use the GetLocalResourceObject or GetGlobalResourceObject method to read resources from a global or local resource file, respectively.

Button1.Text = GetLocalResourceObject("Button1.Text").ToString();
Button1.Text = GetGlobalResourceObject("Button1.Text").ToString();

Note: Each global resource gets compiled into a class under the Resources namespace. You can use this class to read resource values using strong typing. For example,


CompanynameLabel.Text = Resources.MyGlobal1.CompanyName;

Steps to Localizw an ASP.NET Application

You can follow these steps to localize your ASP.NET application:

Decide how you will let the users specify their culture of choice. You either could give them an explicit way to choose or take the culture specified in their browser, or a combination of the two.


Move the things you want to localize, such as the strings displayed on the Web pages and maybe even the images shown, to local or global resource files.


Change your Web page or code-behind classes (or both) to get their localized property values from resource files instead of hard-coding them.


Get the resources translated into the cultures you want to support.


Some Useful Links


  1. MSDN Documentation covers the topic in detail: Globalizing and Localizing Applications

  2. The Localization section in the Quickstart Tutorials (includes very good samples): Internationalizing Your Application

  3. The best practices to follow when developing world-ready applications: Best Practices for Developing World-Ready Applications

BACKLINK