Pages

Advertisement

Friday, August 31, 2007

Security - Lifehacker

 

Secure your laptop with Laptop Alarm

Windows only: Next time you leave your laptop unattended, turn on freeware program Laptop Alarm. Laptop Alarm sets off an alarm to alert you any time someone tries to log off, shut down, or disconnect your power supply or USB mouse without entering your password. Laptop Alarm is similar to Mac-only alarm iAlertU but—let's be honest—with much less pizazz. (iAlertU is motion-sensing, for chrissake!) Either way, neither software is foolproof by any means. Laptop Alarm won't prevent anyone from grabbing your computer and running, but at the very least it might sound off the alarm in enough time to give you a fighting chance to chase down the bad guy. (The only true method of theft prevention is never leaving your laptop alone.) Laptop Alarm is freeware, Windows only. For more advanced laptop theft fun, check out LaptopLock.

Security - Lifehacker

Thursday, August 30, 2007

ASP.NET Tip: Control the Layout of Your Input Forms

Although many of your input forms will have a fixed number of fields, in some cases you'll need to vary the number and configuration of your controls. This article demonstrates how to control the layout of your form using a Repeater control and the ItemDataBound event to set various attributes on the controls.

The example form has a simple table that includes the following Repeater control:

<asp:Repeater ID="rptFields" runat="server">
<ItemTemplate>
<tr>
<td align="right" ID="tdPrompt" runat="server"
nowrap><%# Eval("Prompt") %>:</td>
<td><asp:TextBox ID="txtValue" runat="server" />
<input type="hidden" id="hdnFieldID"
value='<%# Eval("pkFieldID") %>'
runat="server" /></td>
</tr>
</ItemTemplate>
</asp:Repeater>

Behind the scenes, a table called FormFields (or something appropriate) has these fields:

pkFieldID
Primary Key, integer

Prompt
String, contains the prompt to prefix the field with

MaxLength
Integer, indicates maximum number of characters for the field

IsRequired
Bit, indicates whether field is required

In the initial Load event for the page, I retrieve a DataTable containing the fields from the database, and then I bind it to the Repeater like this:

rptFields.DataSource = fieldsDataTable;
rptFields.DataBind();
The key event handler to make this work is the ItemDataBound event handler. It looks something like this:

void rptParameters_ItemDataBound(object sender,
RepeaterItemEventArgs e)
{
if (e.Item.ItemType != ListItemType.Item && e.Item.ItemType
!= ListItemType.AlternatingItem)
return;

DataRow dr = ((DataRowView)e.Item.DataItem).Row;
HtmlTableCell td = (HtmlTableCell)e.Item.FindControl("tdPrompt");

if (Convert.ToBoolean(dr["IsRequired"]))
td.Attributes["class"] = "popupreq";
else
td.Attributes["class"] = "popuptext";

TextBox txt = (TextBox)e.Item.FindControl("txtValue");
txt.MaxLength = Convert.ToInt32(dr["MaxLength"]);

}

The first thing you do is make sure that you are looking at an "Item" or "Alternating Item" template. This is because the ItemDataBound event handler is called for the Header and Footer templates, which you don't care about for this code. You then get the DataRow for the current item being bound. Next, you find the table cell that contains the prompt text and change the style to either 'popupreq' or 'popuptext'. In this example, the popupreq style uses bold face, while popuptext uses a non-bold font.

The next step is to find the TextBox control and configure the maximum length of the field. The hidden field in the HTML portion of the page will be populated automatically with the primary key of the field record, which will be used in the back-end code to properly store the data to the database.

When the user hits the Submit button, ASP.NET keeps the values that the user enters into each field and makes them available to the PostBack code. Reading the values is similar to setting them in ItemDataBound, as this snippet shows:

foreach (RepeaterItem ri in rptParameters.Items)
{
hdn = (HtmlInputHidden)ri.FindControl("hdnFieldID");
txt = (TextBox)ri.FindControl("txtValue");
// Read hdn.Value or txt.Text for persistence code
}

The reason you stored the field ID is because this data will generally need to be put into a table related to the data being edited. The easiest way I've found to do this is to fill the hidden field with the field's number, which you can use later in a stored procedure or other method to save the field's data to the database.

ASP.NET Tip: Exporting Data to Excel

A common request of users is to be able to download data for use in Microsoft Excel. This is actually fairly easy to accomplish without a lot of extra work. I do most of my database work with DataTable objects, as I don't need the overhead of a DataSet. I also have a Database wrapper class that is able to easily retrieve data from a SQL Server database. Once I use my function to get my DataTable, I can start exporting the data. For this example, however, I'll use the traditional SqlDataAdapter method to retrieve the data initially.

There are a few tricks to making this work. First, you have to create an ASPX page with no HTML content in it. The only thing that should be in the ASPX page is the Page directive at the top. The second trick is to clear the existing content and send down a new content type. For Excel, you use a content type of application/vnd.ms-excel. This tells your Web browser to expect something that can be handled within Excel. The third trick is to send down the data tab-delimited per column and a newline at the end of each row. After that, Excel does the rest of the work for you.

Here's the code:

protected void Page_Load(object sender, EventArgs e)
{
SqlConnection cn = new SqlConnection("yourconnectionstring");
cn.Open();
SqlDataAdapter da = new SqlDataAdapter("SELECT * FROM Users", cn);
DataTable dt = new DataTable();
da.Fill(dt);
cn.Close();

Response.Clear();
Response.ContentType = "application/vnd.ms-excel";
string sep = "";
foreach (DataColumn dc in dt.Columns)
{
Response.Write(sep + dc.ColumnName);
sep = "\t";
}
Response.Write("\n");

int i;
foreach (DataRow dr in dt.Rows)
{
sep = "";
for (i = 0; i < dt.Columns.Count; i++)
{
Response.Write(sep + dr[i].ToString());
sep = "\t";
}
Response.Write("\n");
}

}

After I open my connection and retrieve my data, I clear the output buffer and send down a new content type, since the default type of text/HTML isn't appropriate in this case. I then loop through the columns and write out the column names, separated by tabs. At the end of the field list, I send down a newline character.

I then loop through the rows of the table and, within each row, loop through the fields. Each field value is printed out in its default format under the appropriate column header. If you have data types, such as Booleans, that should be printed in a different format, you can look at the dt.Columns collection to help determine which data type each field is and use an appropriate conversion function.

The result of running this page is a prompt to open or save the Excel spreadsheet. You can use this code with any query and any database connection. Once you've got the content cleared and the content type set, Excel does the hard work of formatting for you.

Redirecting Configuration with a Custom Provider

A common issue with Web applications is that applications are managed in various environments. Aside from development environments, you may have test servers, staging servers, live production servers, and potentially warm backup servers. If you store selected Web configuration sections in a central location (such as a central file share or a central configuration database), you have a more manageable solution and, depending on how you implement this, a more secure solution as well.

With ASP.NET 2.0, you can write a custom protected configuration provider that determines information about the current server and the currently running application. A custom provider can reach out to a central repository of configuration information and return the appropriate configuration information. When you have a group of individuals who manage the configuration data for live production servers, it is probably much easier to have such a group manage updates to a single database as opposed to synchronizing configuration files across multiple machines.

Implementing a custom protected configuration provider requires you to derive from the System.Configuration.ProtectedConfigurationProvider class. As you can see, the class signature is very basic:

    public abstract class ProtectedConfigurationProvider : ProviderBase
{
public abstract XmlNode Encrypt(XmlNode node);
public abstract XmlNode Decrypt(XmlNode encryptedNode);
}

For a sample provider that demonstrates redirecting configuration to a database, you implement only the Decrypt method because this is the method used at runtime to return configuration data to the caller. If you store more complex data inside your protected configuration format, implementing the Encrypt method will make life easier when storing configuration sections in a custom data store.

First look at what a "protected" configuration section in a web.config file will look like using the custom provider:

<membership configProtectionProvider="CustomDatabaseProvider">

<EncryptedData>
<sectionInfo name="membership" />
</EncryptedData>
</membership>

The <membership /> section references a protected configuration provider. Instead of the actual definition of the <membership /> section though, the <EncryptedData /> element is common to all protected configuration sections. However, what is enclosed within this element is determined by each protected configuration provider. In this case, to keep the sample provider very simple, the protected data consists of only a single element: a <sectionInfo /> element.

Unlike protected configuration providers that blindly encrypt and decrypt data, this sample provider needs to know the actual configuration section that is being requested. The custom provider needs to know what section is really being requested because its purpose is to store configuration data in a database for any arbitrary configuration section. The name attribute within the <sectionInfo /> element gives the custom provider the necessary information. Although this is just a basic example of what you can place with <EncryptedData />, you can encapsulate any kind of complex data your configuration provider may need within the XML.

The custom provider will store configuration sections in a database, keying off of a combination of the application's virtual path and the configuration section. The database schema that follows shows the table structure for storing this:

create table ConfigurationData (
ApplicationName nvarchar(256) NOT NULL,
SectionName nvarchar(150) NOT NULL,
SectionData ntext
)
go
alter table ConfigurationData
add constraint PKConfigurationData
PRIMARY KEY (ApplicationName,SectionName)
Go

Retrieving this information will similarly be very basic with just a single stored procedure pulling back the SectionData column that contains the raw text of the requested configuration section:

create procedure RetrieveConfigurationSection
@pApplicationName nvarchar(256),
@pSectionName nvarchar(256)
as
select SectionData
from ConfigurationData
where ApplicationName = @pApplicationName
and SectionName = @pSectionName
go

Because the custom protected configuration provider needs to connect to a database, a connection string must be included within the definition of the provider.

<configProtectedData>
<providers>
<add name="CustomDatabaseProvider"
type="CustomProviders.DatabaseProtectedConfigProvider,
CustomProviders"
connectionStringName="ConfigurationDatabase"
/>
</providers>
</configProtectedData>

The sample provider configuration looks similar to the configurations for the RSA and DPAPI protected configuration providers that ship in the 2.0 Framework. In this case, however, the custom provider requires a connectionStringName element so that it knows which database and database server to connect to. The value of this attribute is simply a reference to a named connection string in the <connectionStrings /> section, as shown here:

<connectionStrings>
<add name="ConfigurationDatabase"
connectionString="server=.;Integrated _
Security=true;database=CustomProtectedConfiguration"/>
</connectionStrings>

When creating your own custom providers, you have the freedom to place any provider-specific information you deem necessary in the provider's <add /> element.

Now that you have seen the data structure and configuration related information, take a look at the code for the custom provider. Because a protected configuration provider ultimately derives from System.Configuration.Provider.ProviderBase, the custom provider can override portions of ProviderBase as well as ProtectedConfigurationProvider. The custom provider overrides ProviderBase.Initialize so that the provider can retrieve the connection string from configuration:

using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Configuration.Provider;
using System.Web;
using System.Web.Hosting;
using System.Web.Configuration;
using System.Xml;
namespace CustomProviders
{
public class
DatabaseProtectedConfigProvider : ProtectedConfigurationProvider
{
private string connectionString;
public DatabaseProtectedConfigProvider() { }
public override void Initialize(string name,
System.Collections.Specialized.NameValueCollection config)
{
string connectionStringName = config["connectionStringName"];
if (String.IsNullOrEmpty(connectionStringName))
throw new ProviderException("You must specify " +
"connectionStringName in the provider configuration");
connectionString =
WebConfigurationManager.ConnectionStrings[connectionStringName] _
.ConnectionString;
if (String.IsNullOrEmpty(connectionString))
throw new ProviderException("The connection string " +
"could not be found in <connectionString />.");
config.Remove("connectionStringName");
base.Initialize(name, config);
}
//Remainder of provider implementation
}
}

The processing inside of the Initialize method performs a few sanity checks to ensure that the connectionStringName attribute was specified in the provider's <add /> element, and that furthermore the name actually points at a valid connection string. After the connection string is obtained from the ConnectionStrings collection, it is cached internally in a private variable.

Of course, the interesting part of the provider is its implementation of the Decrypt method:

public override XmlNode Decrypt(XmlNode encryptedNode)
{
//Application name
string applicationName = HostingEnvironment.ApplicationVirtualPath;
XmlNode xn =
encryptedNode.SelectSingleNode("/EncryptedData/sectionInfo");
//Determine the configuration section to retrieve from the database
string sectionName = xn.Attributes["name"].Value;
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand cmd =
new SqlCommand("RetrieveConfigurationSection", conn);
cmd.CommandType = CommandType.StoredProcedure;
SqlParameter p1 =
new SqlParameter("@pApplicationName", applicationName);
SqlParameter p2 = new SqlParameter("@pSectionName", sectionName);
cmd.Parameters.AddRange(new SqlParameter[] { p1, p2 });
conn.Open();
string rawConfigText = (string)cmd.ExecuteScalar();
conn.Close();
//Convert the string information from the database into an XmlNode
XmlDocument xd = new XmlDocument();
xd.LoadXml(rawConfigText);
return xd.DocumentElement;
}
}

The Decrypt method's purpose is take information about the current application and information available from the <sectionInfo /> element and use it to retrieve the correct configuration data from the database.

The provider determines the correct application name by using the System.Web.Hosting.HostingEnvironment class to determine the current application's virtual path. The name of the configuration section to retrieve is determined by parsing the <EncryptedData /> section to get to the name attribute of the custom <sectionInfo /> element. With these pieces of data the provider connects to the database using the connection string supplied by the provider's configuration section.

The configuration data stored in the database is just the raw XML fragment for a given configuration section. For this example, which stores a <membership /> section in the database, the database table just contains the text of the section's definition taken from machine.config stored in an ntext field in SQL Server (i.e. just copy the <membership /> section from machine.config and insert it into the sample database table's SectionData column). Because protected configuration providers work in terms of XmlNode instances, and not raw strings, the provider converts the raw text in the database back into an XmlDocument, which can then be subsequently returned as an XmlNode instance. Because the data in the database is well-formed XML, the provider can just return the DocumentElement for the XmlDocument.

What is really powerful about custom protected configuration providers is that the Framework's configuration system is oblivious to how the configuration information is obtained. For example the following configuration code works unchanged even though the <membership /> section is now being retrieved from a database.

MembershipSection ms =
(MembershipSection)ConfigurationManager.GetSection(
"system.web/membership");

This is exactly what you would want from protected configuration. Nothing in the application code changes despite the fact that now the configuration section is stored remotely in a database as opposed to locally on the file system.

One caveat to keep in mind with custom protected configuration providers is that after the data is physically stored outside of a configuration file, ASP.NET is no longer able to automatically trigger an app-domain restart whenever the configuration data changes. With the built-in RSA and DPAPI configuration providers, this isn't an issue because the encrypted text is still stored in web.config and machine.config files. ASP.NET listens for change notifications and triggers an app-domain restart in the event the encrypted sections in any of these files change.

However, ASP.NET does not have a facility to trigger changes based on protected configuration data stored in other locations. For this reason, if you do write a custom provider along the lines of the sample provider, you need to incorporate operational procedures that force app-domains to recycle whenever you update configuration data stored in locations other than the standard file-based configuration files.

ASP.NET Tip: Persistent Logins Under ASP.NET 2.0

For some sites, allowing users to "save their passwords" is not a major security concern, and it speeds up the user's ability to interact with the site. Assuming your site security can allow for persistent logins, this feature also is easy to provide. Under ASP.NET 1.x, the built-in FormsAuthentication enabled this very easily: it simply specified a True argument when the user ID was saved using FormsAuthentication.RedirectFromLoginPage.

However, ASP.NET 2.0 has changed how the forms authentication tickets work. Simply using the same method with a True argument won't actually persist the cookie. If you want to have a significantly longer timeout for your forms authentication ticket, the code in this tip performs the same steps as the built-in FormsAuthentication methods but gives you more control over the specifics of how it works.

For starters, you'll need to add a block to your Web.config to enable FormsAuthentication:

<authentication mode="Forms">
<forms name="MyApplication" loginUrl="/login.aspx" />
</authentication>

If you want to switch into SSL mode for the login, you can specify the full URL, including the "https://" prefix in the loginUrl parameter. Also add an authorization section to lock down your entire site or virtual directory:

<authorization>
<deny users="?"/>
</authorization>

The code in your login form, after you've done your own validation to see if the user can log into your application, looks like this:

FormsAuthenticationTicket t =
new FormsAuthenticationTicket(1, userID,
DateTime.Now, DateTime.Now.AddMonths(3),
chkSave.Checked, userID.ToString(),
FormsAuthentication.FormsCookiePath);

string encTicket = FormsAuthentication.Encrypt(t);

HttpCookie c = new HttpCookie(FormsAuthentication.FormsCookieName,
encTicket);

if (chkSave.Checked)
c.Expires = DateTime.Now.AddMonths(3);

Response.Cookies.Add(c);

In this example, the userID variable is the value that will be available if you look at User.Identity.Name after the user has logged in. On my page, chkSave is a check box that lets the user indicate whether or not to save the password. That true/false value is passed into the FormsAuthenticationTicket constructor to mark the ticket as persistent or not. After you get the ticket back, encrypt it and then put it into the designated cookie.

If the user has chosen to save the cookie, the cookie needs to be assigned an expiration date. In this case, I'm using three months as the expiration timeframe.

ASP.NET Tip: Testing Web Sites with HttpWebRequest

As a third type of verification, I check certain Web sites to make sure that the server is up, running, and not generating any Web errors. In some cases, Microsoft's Web server produces a Web page even if it has an error. If you don't check the content of that page, it might look as though the server is actually up and running. As a result, I also have a type of check that simulates a browser visiting the page and reading the content. I use the HttpWebRequest and HttpWebResponse classes, as well as some other network I/O code. The following code loops through some good addresses and one bad address to read the TITLE tag from each page:

ArrayList addrs = new ArrayList();
addrs.Add("http://iis02.northcomp.com");
addrs.Add("http://iis03.northcomp.com");
addrs.Add("http://ncs01.northcomp.com");
addrs.Add("http://blahblahblah.northcomp.com");

foreach (string s in addrs)
{
try
{

HttpWebRequest req = (HttpWebRequest)WebRequest.Create(s);
HttpWebResponse resp = (HttpWebResponse)req.GetResponse();
Stream st = resp.GetResponseStream();
StreamReader sr = new StreamReader(st);
string buffer = sr.ReadToEnd();
int startPos, endPos;
startPos = buffer.IndexOf("&lt;title>",
StringComparison.CurrentCultureIgnoreCase) + 7;
endPos = buffer.IndexOf("&lt;/title>",
StringComparison.CurrentCultureIgnoreCase);

string title = buffer.Substring(startPos, endPos - startPos);
Console.WriteLine("Response code from {0}: {1}", s,
resp.StatusCode);
Console.WriteLine("Page title: {0}", title);
sr.Close();
st.Close();
}
catch (Exception ex)
{
Console.WriteLine("Error connecting to {0}.", s);
Console.WriteLine("Exception:");
Console.WriteLine(ex.ToString());
}

}
Console.WriteLine("Web site check completed.");

 


The basic construction of the code is similar to my other tips covering Ping and the TcpClient class; however, reading data is somewhat different. I first create an HttpWebRequest by using the WebRequest Create method. I immediately get the HttpWebResponse from the request and start reading the data. I do this by using a regular StreamReader class, and the results are dumped into a string. I then look at the string for the TITLE tag and the end of that tag, and then pull the results into a separate string. The assumption here is that if I see the appropriate TITLE tag, the page is functioning properly. In addition, the StatusCode will display as OK for the first three addresses. The fourth generates an error and is trapped appropriately.

You can modify this code to look for any string in the page. For instance, you might want to verify that the last line or HTML tag on the Web page is present, which would imply that the page completed without any errors.

RFID Programming Made Simple and Cheap

Radio Frequency Identification Device (RFID) has been getting a ton of coverage in the press ever since Wal-mart stated that they were going to require their suppliers to tag products. Over the past few years, the ability to use RFID has gotten not only cheaper, but also easier to use. By the end of this article, you'll see just how easy it has gotten to implement a passive RFID system that hooks into your applications. I use C# and .NET in my example, but you could also use Java or several other languages to do the same thing.

RFID generally involves a reader and a set of tags. The reader scans a tag and pulls information from it. This information can be used to uniquely identify the tag and thus identify whatever is associated to the given tag.

There are a variety of readers that can be used. These range from readers for passive tags to readers for active (non-passive) tags.

Active tags have a power source within them, such as a small battery. As such, they actually transmit can their information a much farther distance than the passive tags. A good example of the use of active tags is in cards used to pay highway toll fees as you drive by.

Passive tags are cheaper and thus are more likely what people are looking to use for solutions such as inventory, ID cards, and other forms of monitoring. Passive tags also come in a variety of shapes and sizes that make them usable in a large number of applications. These can range from small stickers that can be stuck to products, to small glass vials that can be used to track liquids. There are even key chains, credit cards, watch bands, and other forms of passive tags. Figure 1 shows some of the passive tags that I've obtained.

The RFID Hardware



Click here for a larger image.

Figure 1: A variety of passive RFID tags

The cost of RFID tags can vary depending on where you buy them and how many you purchase. Most of the tags in Figure 1 cost me between $1.20 and $3, but I purchased them individually. If you buy them in large quantities, the cost will obviously drop quickly. You can find these tags and other ones from a variety of sources. I obtained these from http://www.trossenrobotics.com/store/c/2964-EM4102.aspx.

In addition to passive versus active, you also have to know the radio wave range of the reader and tags you are using. The tags in Figure 1 are all passive tags that operate at the 125 KHz frequency. As such, they use a reader that can recognize this frequency. I used an RFID reader from Trossen Robotics . Figure 2 shows the small PhidgetsRFID passive tag reader.



Click here for a larger image.

Figure 2: The Phidgets RFID reader

This particular reader plugs into a USB port and will read passive tags that operate at 125khz. You can find this reader retails for about $65 US from either Phidgets or Trossen Robotics.

Programming RFID

Each RFID tag contains a unique ID and possibly other information. As mentioned with the readers, there are passive tags and active tags. A passive tag generally contains an antenna—actually a coil of wire—that when put near a reader creates a small charge that is enough to cause the tag to transmit its unique ID. This small charge, however, is minuscule, so a passive tag generally has to be within a couple of inches of a reader to work.

The unique tag ID is the key to using RFID in a program. The unique ID what sets one tag apart from all other tags. If you are tracking inventory, you can place a tag on an item and then associate the ID of the tag to that item. You've then given each item a unique key to access it by.

As an example, a credit card tag can be used as a membership card. Within your membership database, you simply need to add an additional field for the Tag ID. Before giving the card to the member, you can scan the card and associate the Tag ID to that member in your membership database. From that point forward, that unique RFID card will be associated to that member in your database. If you scan that card, you can use the retrieved unique tag ID to then search your membership database for the RFID tag number and then get the associated membership information.

This example should not sound too far off. If you have a "swipe card" that you use to access an office building, it is most likely that you are already using RFID in a manner similar to what I just described for a membership database.

Downloads

  • RFIDTest.zip -
  •  

    RFID in Action

    To repeat the key point, using RFID is simply a matter of reading the associated tag to get the unique ID. Once you have the unique ID, it is simple data. You need a reader to do the reading and tags to read. In the rest of this article, I'll walk through what it takes to use the Phidgets RFID reader mentioned above to read standard 125 KHz tags.

    The RFID Hardware

     

    The Phidgets RFID reader simply plugs into a USB port. Once plugged in, the hardware side of the example is set up. It can't get easier than that.

    Reading a tag, however, does require a few more steps. For you to be able to tap into the hardware, you will need to install the Phidgets application programming interface (API). You can download this code from here. You also can download example programs and documentation from the site as well. While I'm using .NET on a Microsoft Windows XP machine, you will also find that there are APIs for Linux, the Mac, and other platforms as well.

    Install the API by running the Phidgets21.msi file. This will install a dynamic link library that you will then be able to reference from your application. I used the 2.1 beta version of the library. If you use a different version, my code may not work because the libraries are not backward compatible. This version, however, seemed much easier to use than prior versions.

    The RFID Program

    Once you have the library installed, you are ready to begin building an RFID application. I'll be using Visual Studio 2005 and C# to build the application shown in Figure 3.

    Figure 3: A simple RFID application

    This application simply reads tags and displays their unique IDs in the form. If you read a tag, it will be shown in the Tag text field while it is being read, and then added to the listbox once the read is complete. In this way, the listbox will maintain a history of what has been read. Figure 4 shows the dialog after the reader has been turned on (by checking the box in the form) and after a few tags have been read.

    Figure 4: Having read a few tags

    To build this application, you start by creating a new C# Windows Application in Visual Studio 2005. You then should add a reference to the Phidgets API you installed earlier.

    Add a reference by going to the Project menu and selecting Add Reference. Choose the COM tag and search for Phidgets Library 2.1. If it isn't there, use the Browse option and locate the DLL on you system. It is most likely to be at C:Program Files\Phidgets\PhidgetsCOM.dll. Once you've added this reference, you are ready to begin building the form.

    Most of the other property values for the controls are left at the defaults. You can modify these in any way you want. I've included the full source code for this application in the download at the end of this article, so if you have trouble creating the form, just pull it from there.

    Within the form class, which I called RFIDReader, I also set up three additional fields:

    RFID   rfid1;
    string lastRFIDTag;
    Int32 TagCtr;

    RFID creates an RFID object called rfid1. This object will be assigned to the RFID reader so that we can pull information from it. The lastRFIDTag string field simply stores the unique ID from the last tag read. Finally, TagCtr is used to store a simple count of the number of tags read. As you could see in Figure 4, this counter is used to display the count on the listbox. After setting up these variables, the lastRFIDTag is set to blanks and the TagCtr should be set to 0 when you first initialize your form variables.

    With a form set up and variables declared, you should do one more thing before getting to the fun code. Add a couple of references to the Phidgets namespaces to the top of your listing:

    using Phidgets;
    using Phidgets.Events;

    Now the fun code begins. To use the RFID reader, you will need to create an RFID object and then open the reader. You also will want to create a few event handlers to control actions that occur from the reader. This can all be done in the Form_Load event with the following code:

    private void Form1_Load(object sender, EventArgs e)
    {
    rfid1 = new RFID();
    rfid1.Attach += new AttachEventHandler(rfid_Attach);
    rfid1.Detach += new DetachEventHandler(rfid_Detach);
    rfid1.RFIDTag += new TagEventHandler(rfid_Tag);
    rfid1.RFIDTagLost += new TagEventHandler(rfid_TagLost);
    rfid1.open();
    }

     


    In this code, the first thing that happens is that an RFID object is created called rfid1. This will be used to attach to the RFID hardware. Before opening the hardware for use, event handlers are created for attaching to the RFID reader (rfid_Attach), detaching from the reader (rfid_Detach), having a tag put near the reader (rfid_Tag), and having a tag move out of the reader's range (rfid_TagLost). After adding these handlers to the rfid1 object, the open method is called to begin using the RFID object.

    To use the RFID reader, in addition to being connected to your machine, it also has to be turned on. The cboxAntenna checkbox in the form shown in Figure 3 is to let you control turning the reader on and off. By checking the box, you will execute the event to turn on the reader. The code to accomplish follows:

    private void antennaCheckBox_CheckedChanged(object sender,
    EventArgs e)
    {
    rfid1.Antenna = cboxAntenna.Checked;
    }

    As you can see, turning on the reader is simply a matter of setting the Antenna property of your RFID object to true. The value returned from cboxAntenna.Checked will either be true or false, so this will set the rfid1 Antenna property to true or false (on or off). Once it is on, you are ready to begin reading tags.

    With the reader attached and turned on, the next important thing is to read a tag. This is done in the rfid_Tag event handler that was added to the rfid1 object in the Form_Load event. The code for this event hander follows:

    void rfid_Tag(object sender, TagEventArgs e)
    {
    txtTag.Text = e.Tag;
    lastRFIDTag = txtTag.Text;
    rfid1.LED = true; // light on
    }

    The tag's unique ID will be passed into the event handler in the TagEventArgs. As you can see, the tag ID comes out of e.Tag and is simply displayed in the txtTag text box by assigning it to the text property. Because this application is only displaying the tag ID, not much is happening here.

    The second line of code within this event sets the lastRFIDTag field to the ID of the tag just read. This field will be used to update the listbox on the form with the Tag ID that was just read. This update, however, won't happen until the tag is moved off the reader. If you did the update here within the rfid_Tag event, the update could happen over and over as long as the tag remained near the reader. That is not the desired affect wanted—I only want to list the read tag once for each time it moves over and away from the reader.

    The last line of code sets the LED property of the RFID reader to true. The Phidgets RFID reader has an LED light on it. By setting this property to true, the light will be turned on whenever the reader is reading a tag.

    A tag may be read over and over as long as it is over the Reader. Once it is moved out of range of the reader, the rfid_TagLost event will happen. The code for the rfid_TagLost event handler follows:

    void rfid_TagLost(object sender, TagEventArgs e)
    {
    txtTag.Text = "";
    rfid1.LED = false; // light off
    lbPrevRFIDTags.Items.Insert(0,
    string.Format("Tag: {0} - {1}", ++TagCtr, lastRFIDTag));
    }

    In this routine, things are simply being cleaned up. The first line clears the textbox because no tag is currently being read. The second line turns off the LED light. The final line of code writes a line in the listbox showing the RFID tag information that was just read.

    In your applications, the rfid_TagLost event would be the location where you could do your tag processing. The rfid_TagLost event will only happen once when the tag leaves the reader area. That makes this a better location for processing application logic than the rfid_Tag event. For example, for a system where members have an RFID membership card, this would be the area in the code where you would do a look up of your database system to find the unique Tag ID and then the associated member information.

    You've now seen all the critical code—what little of it that was required. The sample also has two other event handlers and a few fields that have not been covered. These events and fields simply display information about the RFID reader such as whether it is attached as well as the serial number and version number of the reader: These events happen when the RFID reader attaches to or detaches from your system.

    void rfid_Detach(object sender, DetachEventArgs e)
    {
    lblAttached.Text = "Not Attached";
    }

    void rfid_Attach(object sender, AttachEventArgs e)
    {
    Phidgets.RFID phid = (Phidgets.RFID)sender;
    lblAttached.Text = " Attached: " + phid.Name;
    lblSerial.Text = " Serial: " + phid.SerialNumber;
    lblVersion.Text = " Version: " + phid.Version;
    }

    Conclusion

    If this is your first time to see the code for reading RFID tags, you may be scratching your head wondering when it will get difficutlt. The answer is, it won't—at least not with the PhidgetsRFID reader and their newest library. It really is just a mater of hooking up a reader, flipping a few property switches, and reading tags. Knowing it is this simple allows you to focus more on how to use RFID rather than on how to code for RFID usage.

    Listing 1: The PhidgetsForm.cs file:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using Phidgets;
    using Phidgets.Events;

    namespace RFIDTest
    {
    public partial class RFIDReader : Form
    {
    RFID rfid1;
    string lastRFIDTag;
    Int32 TagCtr;

    public RFIDReader()
    {
    InitializeComponent();
    lastRFIDTag = "";
    TagCtr = 0;
    }

    private void Form1_Load(object sender, EventArgs e)
    {
    rfid1 = new RFID();
    rfid1.Attach += new AttachEventHandler(rfid_Attach);
    rfid1.Detach += new DetachEventHandler(rfid_Detach);
    rfid1.RFIDTag += new TagEventHandler(rfid_Tag);
    rfid1.RFIDTagLost += new TagEventHandler(rfid_TagLost);
    rfid1.open();
    }

    void rfid_Tag(object sender, TagEventArgs e)
    {
    txtTag.Text = e.Tag;
    lastRFIDTag = txtTag.Text;
    rfid1.LED = true; // light on
    }

    void rfid_TagLost(object sender, TagEventArgs e)
    {
    txtTag.Text = "";
    rfid1.LED = false; // light off
    //write held Tag ID to listview
    lbPrevRFIDTags.Items.Insert(0,
    string.Format("Tag: {0} - {1}", ++TagCtr, lastRFIDTag));
    }

    void rfid_Detach(object sender, DetachEventArgs e)
    {
    lblAttached.Text = "Not Attached";
    }

    void rfid_Attach(object sender, AttachEventArgs e)
    {
    Phidgets.RFID phid = (Phidgets.RFID)sender;
    lblAttached.Text = "Attached: " + phid.Name;
    lblSerial.Text = " Serial: " + phid.SerialNumber;
    lblVersion.Text = " Version: " + phid.Version;
    }

    private void antennaCheckBox_CheckedChanged(
    object sender, EventArgs e)
    {
    rfid1.Antenna = cboxAntenna.Checked;
    }
    }
    }

     


    Downloads

  • RFIDTest.zip -
  • Localization Made Easy with ASP.NET 2.0

    Do you have an ASP.NET application ready and now you want to translate it into different languages, or are you developing a new application for which you want to support localization? If yes, ASP.NET 2.0 makes it easier to do so than ever before.

     

    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


    3. 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.



    1. 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.
    2. 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.

    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

    uppose 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.