Click here to Skip to main content
Click here to Skip to main content

ASP.NET Ajax Chat Application

, 9 Mar 2008
Rate this:
Please Sign up or sign in to vote.
A chat room page using Ajax and LINQ to XML

Introduction

I've developed a simple chat web application that deals with XML files to store information about online chatters, using ASP.NET, C#, LINQ to XML, Ajax -Anthem Framework-.

Chat UI

Technique

The application includes a class to handle the XML file by some methods to create the XML or load it, to save information, modify it, or remove it. The public methods of this class are:

void Join(string userName, DateTime dateTime)
void Say(string userName, string msg, DateTime dateTime)
void Leave(string userName, DateTime dateTime)

List< string > GetOnlineNames()
int GetNumberOfOnlines()
List< string > GetMessagesHistory()

The Client-side calls (asynchronously using Ajax) these methods Join(), Say(), Leave().
And a timer on the Server-side ticks every fixed time (2 seconds in our app.), to read the XML (by calling these methods GetOnlineNames(), GetNumberOfOnlines(), GetMessagesHistory()) and refresh our controls (asynchronously).

The Code

Starting with our XHandle.cs class which is like a chat room, let's see its members:

using System.Linq;
using System.Xml.Linq;
using System.Collections.Generic;

public class XHandle
{
    private string _path;
    private string _xPageName;
    private string _FullPath;

    private XDocument _xDoc;
    private bool _xDocCreated;

    private XElement _usersRoot;
    private int _onlineUsers;

    ....
}

The constructor's job is to:

Know the path of the App_Data folder (_path), and the name of the XML file (_xPageName), to set the _FullPath that we will be using shortly.
Check to see if the XML file is already created, or create it if it is not.

public XHandle(string _path)
    {
        this._path = _path;
        this._xDocCreated = false;
        this._xPageName = "Default";
        this._FullPath = this._path + @"\" + this._xPageName + ".xml";
        this._onlineUsers = 0;

        //Check the XML page if already exists
        LoadXPage();

        //or create it if it doesnt
        if (!_xDocCreated)
        {
            CreateXPage();
        }
    }

    public XHandle(string _path, string ChatRoomName)
    {
        this._path = _path;
        this._xDocCreated = false;
        this._xPageName = ChatRoomName;
        this._FullPath = this._path + @"\" + this._xPageName + ".xml";

        //Check the XML page if already exists
        LoadXPage();

        //or create it if it doesnt
        if (!_xDocCreated)
        {
            CreateXPage();
        }
    }

Private Methods

The LoadXPage() and CreateXPage() are private methods to load or create the XML file and set the _xDocCreated bool to true or false:

    private void CreateXPage()
    {
        _xDoc = new XDocument();
        XDeclaration dec = new XDeclaration("1.0", "utf-8", "yes");
        _xDoc.Declaration = dec;
        _usersRoot = new XElement("users");
        _xDoc.Add(_usersRoot);
        _xDoc.Save(_FullPath);
        _xDocCreated = true;
    }

    private void LoadXPage()
    {
        try
        {
            _usersRoot = XElement.Load(_FullPath);
            _xDocCreated = true;
        }
        catch
        {
            _xDocCreated = false;
        }
    }

Creating the XML file will end up with this:

XML File

Public Methods

When a user joins the chat, we save their information as name, message, and dateTime, when a user says something we also save his/her information as name, message, and dateTime, and when user leaves, we just remove any information that corresponds to his/her name.
This is the XML file when a user named Ahmed joins the chat:

join

This is the XML file when a user named Ahmed says hello:

say

And finally this is the XML file when the user(s) leave:

XML File

The three methods Join(..), void Say(..), void Leave(..):

    public void Say(string userName, string msg, DateTime dateTime)
    {
        XElement user = new XElement("user");
        XElement elementName = new XElement("name", userName);
        XElement elementLastMsg = new XElement("message", msg);
        XElement elementDate = new XElement("date", dateTime.ToString());
        user.Add(elementName);
        user.Add(elementLastMsg);
        user.Add(elementDate);
        _usersRoot.Add(user);
        _usersRoot.Save(_FullPath);
    }

    public void Join(string userName, DateTime dateTime)
    {
        string systemMsg = userName + " joined chat room.";
        this.Say(userName, systemMsg, dateTime);
    }

    public void Leave(string userName, DateTime dateTime)
    {
        var user = from o in _usersRoot.Elements("user")
                   where (string)o.Element("name").Value == userName
                   select o;

        user.Remove();

        _usersRoot.Save(_FullPath);
    }

Other public methods are methods to get the names, number of all online chatters, and the messages history.

    public List< string> GetOnlineNames()
    {
        List< string> names = new List< string>();
        var users = (from o in _usersRoot.Elements("user")
                    select o).Distinct();

        foreach (var user in users)
        {
            if (!names.Contains(user.Element("name").Value))
            {
                names.Add(user.Element("name").Value);
            }
        }
        _onlineUsers = names.Count;

        return names;
    }

    public int GetNumberOfOnlines()
    {
        var users = (from o in _usersRoot.Elements("user")
                     select o).Distinct();
        List< string> names = new List< string>();
        foreach (var user in users)
        {
            //Filter the names to avoid duplicates
            if (!names.Contains(user.Element("name").Value))
            {
                names.Add(user.Element("name").Value);
            }
        }
        if (names.Count > 0)
        {
            _onlineUsers = names.Count;
            return names.Count;
        }
        _onlineUsers = 0;
        return 0;
    }

    public List< string> GetMessagesHistory()
    {
        List< string> messages = new List< string>();
        var users = (from o in _usersRoot.Elements("user")
                     where o.Element("message").Value != string.Empty
                     orderby DateTime.Parse(o.Element("date").Value) ascending
                     select o).Distinct();
        foreach (var user in users)
        {
            string fullString = user.Element("name").Value +
				" : " + user.Element("message").Value;
            if (!messages.Contains(fullString))
            {
                messages.Add(fullString);
            }
        }
        return messages;
    }

Well, now we are done with our class, we have the ability to get the online names, the number of them, and the messages history (this means if someone started the chat at x time and another one joined the chat after 30 minutes from x, the last one will get the messages history since the chat has been started...).

Moving to ChatRoom.aspx

Design

In the Page_Load event, we are going to:

  • Register the page to the Anthem Manager to allow it to be called from client-side script.
  • Get an instance of the XHandle class.
  • Get the number of online chatters if there are any.
  • Set a fixed time (may be 2 seconds) as the timer interval.
  • Subscribe to the Tick event.
  • Attach the refreshing controls methods to delegates, so we can invoke them asynchronously.
public partial class ChatRoom : System.Web.UI.Page
{
    XHandle xmll;

    List< string> names;
    List< string> msgs;

    private delegate void AsyncCallingNames();
    private delegate void AsyncCallingMessages();

    AsyncCallingNames callerNames;
    AsyncCallingMessages callerMessages;

    protected void Page_Load(object sender, EventArgs e)
    {
        Anthem.Manager.Register(this);

        xmll = new XHandle(Server.MapPath("App_Data"), "FirstChatRoom");
        //Get chat room name from user or chat admin

        int x = xmll.GetNumberOfOnlines();
        if (Session["userName"] != null)
        {
            LabelError.Text = "Online, Users Online: " + x.ToString();
        }
        else
        {
            LabelError.Text = "Offline, Users Online: " + x.ToString();
        }

        Timer1.Interval = 2;
        //I set it to 1 second, and it worked well

        Timer1.Tick += new EventHandler(Timer1_Tick);

        callerNames = new AsyncCallingNames(this.RefreshListNames);
        callerMessages = new AsyncCallingMessages(this.RefreshMessages);
    }

    .....
}

Now we have two jobs:

  1. Join, say and leave, This is done by:
    • Join button click event handler
    • Send button click event handler
    • onunload client-side script event handler
        protected void ButtonJoin_Click(object sender, EventArgs e)
        {
            if (Session["userName"] == null)
            {
                Session["userName"] = TextBoxName.Text.ToString();
                xmll.Join(Session["userName"].ToString(), DateTime.Now);
    
                TextBoxName.Enabled = false;
    
                Timer1.StartTimer();
    
                TextBoxType2.Focus();
            }
        }
        protected void ButtonSend_Click(object sender, EventArgs e)
        {
            if (Session["userName"] != null)
            {
                string name = (string)Session["userName"];
                string msg = TextBoxType2.Text.ToString();
                xmll.Say(name, msg, DateTime.Now);
                TextBoxType2.Text = "";
                TextBoxType2.Focus();
            }
            else
            {
                LabelError.Text = "You have to join with a name first..";
            }
        }
    
        [Anthem.Method]
        public void Leave()
        {
            Timer1.StopTimer();
    
            if (Session["userName"] != null)
            {
                string name = (string)Session["userName"];
                xmll.Leave(name, DateTime.Now);
    
                LabelError.Text = "Offline";
            }
        }
  2. Refresh our controls in timer tick event handler:
    • ListBox of chatters names
    • ListBox of messages
        void Timer1_Tick(object sender, EventArgs e)
        {
            if (Session["userName"] != null)
            {
                IAsyncResult resultN = callerNames.BeginInvoke(null, null);
                if (!resultN.IsCompleted)
                {
                    callerNames.EndInvoke(resultN);
                }
    
                IAsyncResult resultM = callerMessages.BeginInvoke(null, null);
                if (!resultM.IsCompleted)
                {
                    callerMessages.EndInvoke(resultM);
                }
                TextBoxType2.Focus();
            }
            else
            {
                Timer1.StopTimer();
                TextBoxType2.Text = "You have to join with a name first..";
            }
        }
    
        private void RefreshListNames()
        {
            ListBox1.Items.Clear();
            names = xmll.GetOnlineNames();
            foreach (var name in names)
            {
                ListBox1.Items.Add(name);
            }
        }
    
        private void RefreshMessages()
        {
            ListBox2.Items.Clear();
            msgs = xmll.GetMessagesHistory();
            foreach (var msg in msgs)
            {
                ListBox2.Items.Add(msg);
            }
        }

Leave()

The last thing is how to call the Leave() method from Client-side script:

< body  önunload="Leave(); return false;">
< script type="text/javascript">
function Leave()
{
    Anthem_InvokePageMethod('Leave', null, null);
}

< /script>

Reports

I'll really appreciate your trial of this code and feedback of any problems you may face to make this application better.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Islam ElDemery
Web Developer Business Development Gate
Egypt Egypt
Blog
Follow on   Twitter

Comments and Discussions

 
QuestionList box of Chat items does not scroll down PinmemberTwipper25-Jun-13 9:38 
QuestionMessageBox and Online Users couting is not clearing after each sessions PinmemberR_Coder29-Apr-13 21:03 
QuestionLogOut Button not clear the cookies Pinmemberparacha118-Apr-13 8:47 
QuestionDatalist Problem Pinmemberparacha17-Feb-13 20:35 
QuestionTextbox clearing problem Pinmemberdarvisunny23-Jan-13 0:45 
Generalreally helpful PinmemberSebahattin Kumpinar21-Nov-12 1:44 
GeneralMy vote of 5 Pinmembergopalbotadra10-Mar-11 15:43 
GeneralNobody In Code Project Implemented one to one chat using asp.net Pinmemberajay_zenta14-Aug-09 7:09 
QuestionPrivate chat rooms? Pinmemberlianaent17-May-09 13:55 
AnswerRe: Private chat rooms? Pinmembersyoumayalew26-Jun-09 9:34 
Generalhi PinmemberMohsen Dalil26-Jan-09 18:32 
Questioncan i run this project on visual studio 2005 ? Pinmemberchandradev13-Dec-08 17:05 
Generalprivate chat Pinmembermcasoni7-Jul-08 22:56 
Generalhi i could not run the project Pinmemberokansarica2-Jul-08 22:34 
Questionweb chat messenger allows 1-to-1 communication between users and also send file or image file. PinmemberMember 29618458-Apr-08 2:17 
Questionprivate chat??? Pinmembershwethashiv29-Mar-08 4:30 
GeneralLinq Question Pinmemberspiderman_5517-Mar-08 4:31 
GeneralRe: Linq Question PinmemberIslam ElDemery18-Mar-08 2:05 
Generaltanx so cool but... Pinmemberbarbod_blue10-Mar-08 22:22 
GeneralRe: tanx so cool but... PinmemberIslam ElDemery10-Mar-08 22:59 
Generali asked about PRIVATE CHAT... Pinmemberbarbod_blue11-Mar-08 2:35 
GeneralRe: i asked about PRIVATE CHAT... PinmemberIslam ElDemery11-Mar-08 2:40 
GeneralRe: i asked about PRIVATE CHAT... Pinmemberbarbod_blue11-Mar-08 2:51 
GeneralRe: i asked about PRIVATE CHAT... PinmemberIslam ElDemery11-Mar-08 3:20 
QuestionConcurrency? Pinmemberjamief10-Mar-08 1:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140902.1 | Last Updated 9 Mar 2008
Article Copyright 2008 by Islam ElDemery
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid