65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET Ajax 聊天应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.15/5 (22投票s)

2008 年 3 月 7 日

CPOL

3分钟阅读

viewsIcon

284824

downloadIcon

21037

使用 Ajax 和 LINQ to XML 的聊天室页面。

引言

我开发了一个简单的聊天 Web 应用程序,它使用 XML 文件来存储有关在线聊天者的信息,使用了 ASP.NET、C#、LINQ to XML、Ajax -Anthem Framework-。

Chat UI

技术

该应用程序包含一个类,该类通过一些方法来处理 XML 文件,以创建 XML 或加载它,保存信息、修改它或删除它。 该类的 public 方法是

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()

客户端调用 (使用 Ajax 异步地) 这些方法 Join()Say()Leave()
服务器端的一个定时器每隔一段时间(在我们的应用程序中为 2 秒)触发一次,读取 XML(通过调用这些方法 GetOnlineNames()GetNumberOfOnlines()GetMessagesHistory())并刷新我们的控件(异步地)。

代码

从我们的 XHandle.cs 类开始,它就像一个聊天室,让我们看看它的成员

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;

    ....
}

构造函数的工作是

了解 App_Data 文件夹的路径 (_path) 和 XML 文件的名称 (_xPageName),以设置我们稍后将使用的 _FullPath
检查 XML 文件是否已经创建,如果未创建,则创建它。

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();
        }
    }

私有方法

LoadXPage()CreateXPage()private 方法,用于加载或创建 XML 文件并将 _xDocCreated 布尔值设置为 truefalse

    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;
        }
    }

创建 XML 文件将得到以下结果

XML File

公共方法

当用户加入聊天时,我们将他们的信息保存为姓名、消息和日期时间,当用户说些什么时,我们也将其信息保存为姓名、消息和日期时间,当用户离开时,我们只删除与其姓名对应的任何信息。
这是一个名为 Ahmed 的用户加入聊天时的 XML 文件

join

这是一个名为 Ahmed 的用户说 hello 时的 XML 文件

say

最后,这是用户离开时的 XML 文件

XML File

三个方法 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);
    }

其他的 public 方法是获取姓名、所有在线聊天者数量以及消息历史记录的方法。

    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;
    }

好了,现在我们完成了我们的类,我们有能力获取在线姓名、他们的数量和消息历史记录(这意味着如果有人在 x 时间开始聊天,而另一个人在 x 时间 30 分钟后加入聊天,则后者将获得自聊天开始以来的消息历史记录...)。

移动到 ChatRoom.aspx

Design

Page_Load 事件中,我们将

  • 注册页面到 Anthem Manager,以允许从客户端脚本调用它。
  • 获取 XHandle 类的一个实例。
  • 如果存在在线聊天者,则获取其数量。
  • 设置一个固定时间(可能是 2 秒)作为计时器间隔。
  • 订阅 Tick 事件。
  • 将刷新控件方法附加到委托,以便我们可以异步调用它们。
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);
    }

    .....
}

现在我们有两项工作

  1. 加入、说和离开,这通过以下方式完成
    • 加入按钮点击事件处理程序
    • 发送按钮点击事件处理程序
    • onunload 客户端脚本事件处理程序
        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. 在计时器 tick 事件处理程序中刷新我们的控件
    • 聊天者姓名的 ListBox
    • 消息的 ListBox
        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()

最后一件事是如何从客户端脚本调用 Leave() 方法

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

< /script>

报告

我将非常感谢您尝试此代码并反馈您可能遇到的任何问题,以使此应用程序更好。

© . All rights reserved.