Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Building an AJAX Based Web Chatting Application using ASP.NET 2.0

0.00/5 (No votes)
19 Feb 2009 1  
ASP .NET Chating Application

Introduction

This article will present you with a Web chatting application, which is based on the famous open source AjaxPro.NET library. Here, we will establish ourselves a nice little multi-user chat application, much like the famous MSN Messenger. We will build the application heavily on JavaScript programming (which is decided by the AjaxPro.NET Framework which supports accessing .NET library through the client-side JavaScript way) under ASP.NET 2.0 and SQL Server Express Edition environments.

Testing Environment

  • Windows XP Professional (with IIS installed)
  • Visual Studio 2005 (plus SQL Server 2005 Express Edition)
  • AjaxPro.NET Framework

Also, you are to be familiar with ASP.NET 2.0 C# programming, SQL Server programming and JavaScript+ DOM. To follow along with the examples, you should download the source files accompanying this article (see the download URL given at the end of the article).

The sample project named AjaxProChat in this article will achieve the listed basic goals.

  • View the on-line chatting friends in real-time
  • Send instantaneous messages to the on-line friends
  • Receive messages in real-time and pop up a friendly MSN-like hint window
  • Switch between the different states—online and offline

Thus, we can easily describe the general flow chart of this chatting application, as shown in Figure 1.

image001.jpg
Figure 1

No doubt that the key lies in the real-time features—state switching in real-time, sending and receiving messages instantaneously, etc. There are many difficulties in tackling all these merely with the traditional web techniques, but now AJAX comes to rescue— all the troubles can be whiled away. Let us now roll up our sleeves by beginning with the database design.

Database Design

With the general goals introduced above, we can now shift our attention to the database design.

In this case, we will design a database named AjaxProChat, which contains three tables—users for holding users info, message for the detailed chatting info, and global_info for recording the changing times in tableusers.

Designing the Tables

Since the source code file with this article provides a whole database, I will omit the relevant creating-table SQL scripts. So, let us directly delve into the table structures. The following three tables give their corresponding structure definitions and field notes, respectively.

Table 1: Structure for Table Users

Field name Type Notes
username varchar(50) NOT NULL, Primary KeyThe login user name
nickname varchar(50) Can be nullThe nickname associated with the user name defined above
password varchar(50) NOT NULL
status int NOT NULL, DEFAULT (0)The user login status—0: offline; 1: online; 2: hidden
logintime datetime Can be NULLThe datetime of the user logging in the last time
logouttime datetime Can be NULLThe datetime of the user logging out the last time
last_msg_id int NOT NULL, DEFAULT (0)The last received message id of the user

Table 2: Structure for Table Message

Field name Type Notes
msg_id int IDENTITY (1,1) NOT NULL, Primary KeyThe auto incremental message id
sender varchar(50) NOT NULLThe user name of the sender
receiver varchar(50) NOT NULLThe user name of the receiver
content varchar(255) NOT NULLThe corresponding chatting content
sendtime datetime NOT NULLThe datetime of sending the message

Table 3: Structure for Table global_info

Field name Type Notes
UserChanged int NOT NULL, DEFAULT (0)

Besides these tables, we have also defined a trigger in table users. This means that whenever there are appending, deleting or updating with the records in it, the trigger should be invoked to add 1 to the fieldUserChanged in table global_info.

Designing the Stored Procedures

To improve the efficiency and simplify the database access, we design five stored procedures inside databaseAjaxProChat. Below we enumerate them and their corresponding functions:

  • GetNewMessage—Fetch the most recent messages which accord with specified conditions
  • GetRecentMsg—Return the chatting info of the two specified most recent users
  • SendMessage—Save the sent messages (the sent and received ones) as new records into the database
  • UserLogin—Log into the system—return zero if successful, otherwise return 1
  • UserLogout—Perform the inverse action of UserLogin

Since you will begin a long journey and I have provided the commented source code for you to download, we will not list their SQL scripts herein any more. But with the story going on, you will quickly conclude that nearly all the .aspx pages are filled with the following call route as depicted in Figure 2.

image002.jpg

Figure 2

Detailing the Solution

Before starting to put AjaxPro.NET into action, let me give you a brief introduction.

AjaxPro.NET is a well-known open source framework, which is based on the server-side techniques and provides support for constructing different versions of .NET Web applications. The framework supports the server-side .NET SDK by a few means via the client-side JavaScript. It can direct the JavaScript requests to the related server-side .NET methods and then the server returns the newly-generated special JavaScript to the browser. Its main functions are listed as below:

  • Access the Session and Application data from the client-side JavaScript
  • Cache the required results
  • Freely use source code
  • Add and modify new methods and properties in the framework without modifying any source code
  • All the classes support the client-side JavaScript to return data, and DataSet can be used from inside the JavaScript
  • Use the HTML controls to access and return data
  • Do not need reload the pages and use the event delegate to access data n
  • Supply only one method for call and dramatically decrease the CPU usage

Now, to use AjaxPro.NET in our ASP.NET 2.0 web applications we should properly configure the web.configfile. Since the downloaded materials have been shipped with a guide and an excellent Visual Studio Add-in—AjaxProVSTemplate.vsi, we will not talk much about the usage of this framework, but focus on the main topic.

In this demo application we mainly provide three web pages—login.aspx, main.aspx, and chatroom.aspx. There is, however, a small trick worthy to be mentioned first.

Index.html—a broker

To hide the browser's menu bar and toolbars in our chatting pages, we use some tricks -building a temporary broker page index.html. The client-side JavaScript in Listing 1 below shows the how-to.

Listing 1

<script language="javascript">
//open a new window
window.open("Login.aspx", "_blank", "location=no;menubar=no;status=no;toolbar=no");
//close the parent window
Close();
// close the window without any prompt
function Close()
{
  var ua = navigator.userAgent;
  var ie = navigator.appName == "Microsoft Internet Explorer" ? true : false;
  if (ie)
  {
    var IEversion = parseFloat(ua.substring(ua.indexOf("MSIE ") + 5, ua.indexOf
      (";", ua.indexOf("MSIE "))))if (IEversion < 5.5)
    {
      var str = '<object id=noTipClose classid=
        "clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">';
      str += '<param name="Command" value="Close"></object>';
      document.body.insertAdjacentHTML("beforeEnd", str);
      document.all.noTipClose.Click();
    }
    else
    {
      window.opener = null;
      window.close();
    }
  }
  else
  {
    window.close()
  }
}
</script>

Login.aspx

Next, comes up the normal login page, login.aspx, and Figure 3 shows you the first impress.

image003.jpg

Figure 3

This web page is the entry point of this web-versioned application, which merely calls the stored procedureUserLogin described before so as to accomplish logging into the system. This procedure is a typical one and we will not depict it, but only one thing should be noted: If the user name entered is not empty and there is not an old one inside the database, then we will simply store it whether its relevant password is empty or not. For more details about this please check out the stored procedure's SQL scripts. So next, we will move to the key code in the file login.aspx.cx.

Listing 2: The Typical ASP.NET 2.0 Server-Side Database Connection Programming for the Login Page

public partial class login: System.Web.UI.Page
{
  private string myConnectionString = ConfigurationManager.ConnectionStrings[
    "msn_Data_ConnStr"].ConnectionString;
  protected System.Data.SqlClient.SqlConnection sqlConnection;
  protected System.Data.SqlClient.SqlCommand sqlCommand;
  protected void Page_Load(object sender, EventArgs e){}
 
  protected override void OnInit(EventArgs e)
  {
    InitializeComponent();
    base.OnInit(e);
  }
  private void InitializeComponent()
  {
    this.sqlConnection = new SqlConnection(myConnectionString);
    this.sqlCommand = new System.Data.SqlClient.SqlCommand();
    this.sqlCommand.CommandText = "dbo.[UserLogin]";
    this.sqlCommand.CommandType = System.Data.CommandType.StoredProcedure;
    this.sqlCommand.Connection = this.sqlConnection;
 
    //specify the parameters of the stored procedure
    this.sqlCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter(
      "@RETURN_VALUE", System.Data.SqlDbType.Int, 4,
      System.Data.ParameterDirection.ReturnValue, false, ((System.Byte)(0)), (
      (System.Byte)(0)), "", System.Data.DataRowVersion.Current, null));
 
    this.sqlCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter(
      "@username", System.Data.SqlDbType.VarChar, 50));
    this.sqlCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter(
      "@password", System.Data.SqlDbType.VarChar, 50));
    this.sqlCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter(
      "@status", System.Data.SqlDbType.Int, 4));
  }
 
  protected void btnLogin_Click(object sender, EventArgs e)
  {
    int iRet =  - 1;
    sqlCommand.Parameters["@username"].Value = tbUsername.Text;
    sqlCommand.Parameters["@password"].Value = tbPassword.Text;
    sqlCommand.Parameters["@status"].Value = Convert.ToInt32
      (ddlStatus.SelectedValue);
 
    try
    {
      sqlConnection.Open();
      sqlCommand.ExecuteNonQuery();
      iRet = Convert.ToInt32(sqlCommand.Parameters["@RETURN_VALUE"].Value);
    }
    catch (SqlException)
    {
      Response.Write("<script>alert('Here,in SqlException');</script>");
        //merely for debugging purpose
    }
    finally
    {
      sqlConnection.Close();
    }
    if (iRet == 0)
    {
      FormsAuthentication.RedirectFromLoginPage(tbUsername.Text, false);
      Response.Redirect("Main.aspx");
    }
    if (iRet == 2)
    {
      FormsAuthentication.RedirectFromLoginPage(tbUsername.Text, false);
      Response.Redirect("Main.aspx");
    }
    else
    {
      lblMessage.Text = "iRet =" + iRet.ToString() +
        "The login failed, please check out your password!";
    }
  }
}

Main.aspx—The Headquarters

Now, let us pay attention to the main controlling page of the chatting system, main.aspx, whose run-time snapshot is shown in Figure 4.

image004.jpg

Figure 4

From the developer's point view, we can divide this part into three different components: login status switching, the users list viewing, and the newest message hinting. So, let us dig into their inner workings one by one.

Switching Between the Login States

At the top of the main page lie the current user name and its login status. It is easy for the user to switch between items Online and Offline via the ListBox control. When the user click the Logout button, the user will log off and the control is switched to the initial login page. Now, let us examine the related inner logic. First, we define an Ajax method—SetUserStatus on the server side, which directly updates the user login status in table users according to the passed arguments—strUsername and iStatus. Listing 3 gives the corresponding coding:

Listing 3

[AjaxPro.AjaxMethod]
public  int SetUserStatus(string strUsername, int iStatus) {
     //hold the returned value
      int iRet = 0;
     //Obtain the database connection string from the file web.config and set up the connection
      string strConnection = ConfigurationManager.ConnectionStrings["msn_Data_ConnStr"].ConnectionString;
      SqlConnection conn = new SqlConnection(strConnection);
 
     //create a new SQLCommand object
      SqlCommand cmd = conn.CreateCommand();
      cmd.CommandText = string.Format(
            "UPDATE users SET status = {0} WHERE username='{1}'",
            iStatus, strUsername
            );
      try{
           //open the data connection
            conn.Open();
           //execute the SQL command-- set the user's status
            iRet = cmd.ExecuteNonQuery();
      }
      catch (SqlException){}
      finally
      {
           //close the database connection
            conn.Close();
      }
      return iRet;
}

Next comes up the relevant client-side function, setUserStatus, which will call the server-side methodsetUserStatus to update the current user login status, as is shown in Listing 4.

Listing 4: The Client-Side JavaScript Method Calls the Server-Side Ajax One

 Collapse Copy Codefunction setUserStatus()
{
      //user name
      var username = el("lblUsername").innerText;
      //status
      var status = el("user_status").value;
      //call the relevant server-side Ajax method
      AjaxProChat.SetUserStatus(username, status);
}

As for logoff, this is performed inside the button Logout-related event handler, lblExit_Click, via the routine ASP.NET event response mechanism. Here we leave out the code listing.

Viewing the User List

Note that, in this case, we take the action of inquiring the database on time to list all the on-line users stuff. Therefore, we have to earnestly count the cost between the user real-time characteristics and the server-side load. Obviously, if the interval for inquiring the database is shorter, then the real-time features of the application will be acceptable, which will put a heavier burden on the server's shoulder. However, if the interval is longer, then there is a lighter load on the server and the merciful real-time characteristics on the client side. So, here we use some sleight: in the database we add a sign—the field UsersChanged in the tableglobal_info which is used to record the times table users to be changed. So, in the code we can judge on time whether the sign has been changed to decide whether to send requests to the server side for updating the on-line users list or not. Here, for brevity, we select to omit the simple server-side code while making room for the client-side programming.

Listing 5

 Collapse Copy Code//update the users list
function refreshUserlist()
{
 //get the value of field UsersChanged from inside table users on the server side
  var changed = AjaxProChat.StatusChanged().value;
 //if table users has changed
  if (changed > changedTimes)
  {
   //write down the current value of field UsersChanged
    changedTimes = changed;
   //obtain the DataTable object to hold the users info
    var arUserlist = AjaxProChat.GetOnlineUserList().value.Tables[0];
   //the <div> object to show the users list
    var divUserlist = el("userlist");
   //remove the old contents
    while (divUserlist.childNodes.length > 0)
    {
      divUserlist.removeChild(divUserlist.childNodes[0]);
    }
   //show the users list
    for (var i = 0; i < arUserlist.Rows.length; i++)
    {
     //the login user name
      var username = arUserlist.Rows[i].username;
     //nickname
      var nickname = arUserlist.Rows[i].nickname;
     //create a <div> object for showing one user message
      var result = document.createElement("div");
     //set the cursor shape hand-like
      result.style.cursor = "pointer";
     //inner patches
      result.style.padding = "2px 0px 2px 0px";
     //mouse click handler--open the chat room window
      result.onclick = function()
      {
       //if the chat room window has not been opened
        if (!sendWindow)
        {
         // open the chat room window--note the passed parameters
          window.showModelessDialog("chatroom.aspx?username=" + username,
            window, "help:no;unadorned:yes;edge:sunken;resizable:yes;status:no")
            ;
        }
      };
 
     //the texts for the user name and the nickname are to be shown inside the <span> object
      var result1 = document.createElement("span");
     //set the mouse in-and-out effect
      result1.onmouseover = function()
      {
        this.style.color = "#205288";
      };
      result1.onmouseout = function()
      {
        this.style.color = "#000000";
      };
     //set show style
      result1.style.textAlign = "left";
      result1.style.fontWeight = "bold";
      result1.style.fontFamily = "Arial, Verdana";
     //show the user name plus the nickname
      result1.innerHTML = username + " (" + nickname + ")";
     //attach the <div> object to DOM
      result.appendChild(result1);
      divUserlist.appendChild(result);
    }
  }
}

Since we have added so many comments on the import line, we only sum up the general process: obtain the most recent users list inside the database via the Ajax method—AjaxProChat.GetOnlineUserList(); fill in the user name and related nickname in turn. As is seen from above, we use much subtle JavaScript programming around the DOM object—div and span.

A MSN-like Popping up Window

To conveniently accept the new messages, we have designed a small and friendly MSN-like hint window.

image005.jpg
Figure 5: The run-time screenshot of the MSN-like hint window

Page main.aspx is responsible for checking on time whether there have been new messages coming in; if so, this small hint window is to be popped up and the current user can click the link in it to reply. As with the functions of the main page, there are also two steps—both the server side and the client side—needed to accomplish such a prompt action. First, we have defined an Ajax method, mainGetNewMessage, who serves to obtain a DataSet of the newest chatting messages. We can see the typical database connection in ASP.NET 2.0 from Listing 6.

Listing 6: Code for the Server-Side Method mainGetNewMessage

 Collapse Copy Code[AjaxPro.AjaxMethod]
public DataSet mainGetNewMessage()
{
  DataSet ds = new DataSet();
  SqlConnection conn = new SqlConnection
    (ConfigurationManager.ConnectionStrings["msn_Data_ConnStr"].ConnectionString);
  SqlCommand cmd = conn.CreateCommand();
  cmd.CommandText = string.Format("GetNewMessage '{0}'", User.Identity.Name);
  SqlDataAdapter da = new SqlDataAdapter(cmd);
  try
  {
    da.Fill(ds);
  }
  catch (SqlException){}
  finally
  {
    conn.Close();
  }
  return ds;
}

Since the code above is the common database operation and is provided with so many notations, we will not give unnecessary details any more.

On the client side, there is accordingly defined a method, checkNewMessage, who is to invoke the server-side method, mainGetNewMessage, and will pop up a hint box whenever there comes a new message. Listing 7 shows the related code snippet.

Listing 7: Code for the Client-Side Method checkNewMessage

 Collapse Copy Codefunction checkNewMessage()
{
  if (!sendWindow)
  {
    var dt = AjaxProChat.mainGetNewMessage().value.Tables[0];
    if (dt.Rows.length > 0)
    {
      var sender = dt.Rows[0].sender;
      var content = DealBrackets(dt.Rows[0].content);
      var MSG1 = new CLASS_MSN_MESSAGE("aa", 200, 120, "Hint:", sender +
        " says: ", content);
      MSG1.oncommand = function()
      {
        if (!sendWindow)
        {
          window.showModelessDialog("chatroom.aspx?username=" + sender, window,
            "help:no;unadorned:yes;edge:sunken;status:no");
        }
      };
      MSG1.rect(null, null, null, screen.height - 50);
      MSG1.speed = 10;
      MSG1.step = 5;
      MSG1.show();
    }
  }
}

Here shows the typical flow of calling an Ajax method on the server side from within JavaScript on the client side. As you may have guessed, the true secret for this hint window lies in the following code snippet.

Listing 8

Collapse Copy Codevar MSG1 = new CLASS_MSN_MESSAGE("aa",200,120,"Hint:",sender + " says: ",content);

Inside the downloadable source code, we define a pop up window class in JavaScript in the fileCLASS_MSN_MESSAGE.js. We are creating an instance of the class CLASS_MSN_MESSAGE and the last line above show the pop up window by calling the show method of the instance. For more details please do research into the relevant JavaScript coding.

Chatroom.aspx

Lastly, we come to the meat and potatoes of this Ajax-based chatting application. The really important window to start chatting is page chatroom.aspx. Let us first look at its run-time screenshot in Figure 6.

image006.jpg
Figure 6

As is seen from Figure 6, it is rather simple. Most of the page is for chatting with friends and the corresponding messages, with a TEXTBOX control for entering the message to send and a basic Send button at the bottom line. You may have already guessed the similar JavaScript programming skill is used for arranging the <div> object and its inner sub items.

For easier comprehension, we have also divided the part into three components: examining the recent messages, sending the messages, and receiving the messages. Again, let us analyze them one after the other.

Examining the recent messages

When the chat room window is loaded, there is a specified number of latest chatting messages to be loaded. For this purpose, an AJAX method—GetRecentMsg is defined on the server side which then invokes the stored procedure GetRecentMsg and finally the required items of chat records return. Listing 9 gives the specific coding of method GetRecentMsg.

Listing 9

 Collapse Copy Code[AjaxPro.AjaxMethod]
public DataSet GetRecentMsg(string strUsername)
{
  DataSet ds = new DataSet();
  SqlConnection conn = new SqlConnection
    (ConfigurationManager.ConnectionStrings["msn_Data_ConnStr"].ConnectionString);
  SqlCommand cmd = conn.CreateCommand();
  cmd.CommandText = string.Format("GetRecentMsg '{0}','{1}', {2}",
    User.Identity.Name, strUsername, 8);
  SqlDataAdapter da = new SqlDataAdapter(cmd);
 
  try
  {
    da.Fill(ds);
  }
  catch (SqlException){}
  finally
  {
    conn.Close();
  }
  return ds;
}

Sending the messages

For this purpose, an AJAX method—SendMessage is defined on the server side which then invokes the stored procedure GetRecentMsg and finally the required items of chat records return. Listing 10 gives the specific coding of method GetRecentMsg.

Listing 10

 Collapse Copy Code[AjaxPro.AjaxMethod]
public DataSet GetRecentMsg(string strUsername)
{
  DataSet ds = new DataSet();
  SqlConnection conn = new SqlConnection
    (ConfigurationManager.ConnectionStrings["msn_Data_ConnStr"].ConnectionString);
  SqlCommand cmd = conn.CreateCommand();
  cmd.CommandText = string.Format("GetRecentMsg '{0}','{1}', {2}",
    User.Identity.Name, strUsername, 8);
  SqlDataAdapter da = new SqlDataAdapter(cmd);
  try
  {
    da.Fill(ds);
  }
  catch (SqlException){}
  finally
  {
    conn.Close();
  }
  return ds;
}

Receiving the messages

Here, certainly should we receive the messages in real time, while the whole process is very much similar to that of the main page, so we do not say more than is needed. But there is still one thing to be noticed, if the chat room window has been open, the new message will be shown directly in it; otherwise there will pop up a small hinting window (see Figure 5) for this new message. This is why the global variable sendWindow is defined inside page main.aspx's client-side JavaScript, as is shown below.

Listing 11: The initial definition of the variable sendWindow

 Collapse Copy Code//……(omitted, inside main.aspx)
<script language="javascript">
//used to mark whether the chat room is open
var sendWindow = false;
//check out whether the user status has been changed
var changedTimes = 0;
//the main procedure
mainLoop();
//……

Although variable sendWindow is defined inside page main.aspx for identifying whether chat room has been already open, it is also used inside page chatroom.aspx. In connection with the definition above, in pagechatroom.aspx there exist the following code snippets.

Listing 12: Variable sendWindow is also used in page chatroom.aspx

 Collapse Copy Code//……(omitted, inside page <span class=Italic>chatroom.aspx</span>)
<body onunload="dialogArguments.sendWindow=false;" onload="dialogArguments.sendWindow=true;">
//……

When page chatroom.aspx is opened for the first time, variable sendWindow is set to true, when functioncheckNewMessage in page main.aspx will not query new messages and the message be displayed directly in the chat room. However, when the web page chatroom.aspx is close, variable sendWindow is set to false, when function checkNewMessage in page main.aspx will invoke the relevant method GetNewMessage in the server side which will inquire new messages via the stored procedure GetNewMessage and finally show the result in the pop up hinting box.

Author's Notes: until now, we have introduced all the web pages and their rough functions in this sample. Due to the strict demands of SQL Server 2005 Express Edition (and of course including SQL Server 2005), I suggest you download and decompress the source code and then configure the database engine with great care. As for the AjaxPro.NET framework, maybe you are a newbie to it, but there is surely noy much difficulty for you in grasping its general usage. Only one thing is to be noticed—I use the latest version of AjaxPro.NET framework in this sample, but it will be ok to follow me with the related schema. Last but not the least, I have tested the demo project under hosted mode as well as under web mode using Microsoft IE. That is all!

Conclusion

OK, in this single article we have achieved our final goal—a simple MSN-like web-based chatting application. On the other hand, as we have seen in this article, only through the prevalent WEB 2.0 technique—AJAX—can this kind of instantaneous software be developed successfully and ultimately put into practical use. And for brevity, there are still quite a few related techniques to be observed thoroughly, such as more strict and practical login approach, far more JavaScript coding to be analyzed, only the surface of what AjaxPro.NET has offered has been scratched, strictly debugging and deployment, etc. Joyfully, this article has just shown you a web 2.0 based demo, so there are still much for you to research into.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here