Basic Chat Using ASP .NET AJAX
| |
The AJAX Chat Sample shows how to build a browser based chat using ASP .NET and AJAX. ASP.NET AJAX is the easiest and most enjoyable way to start writing asynchronous Web applications using ASP.NET. The official ASP.NET AJAX site is here, and you will want to make sure you have downloaded the last version of the framework and the control toolkit before continuing with this example.
|
|
Tyler Schlegel
Difficulty: Easy
Time Required: 1-3 hours
Cost: Free
Hardware:
|
The Code
Here’s an overview of the basic chat logic; it’s very simple—
- A user accesses a page, and a new Chatter object is initialized
- The page class determines which Chats are available in the current application context on the server
- The page class calls the Chatter’s Join method to associate the Chatter with the appropriate Chat object
- The Chatter begins calling the Chat’s SendMessage method to post new messages
Create an ASP.NET AJAX-Enabled Web Site and Enable Partial Rendering
To begin, create a new ASP.NET AJAX-Enabled Web Site in Visual Studio 2005

The Default.aspx file created in the new project should contain the following:
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
</form>
Before continuing, add the EnablePartialRendering="true" attribute to the ScriptManager control. This enables you to update regions of the page individually by using UpdatePanel controls (more on that later!). You will also want to copy the App_Code folder from this examples downloadable files to your project directory. The two class files “Chat.cs” and “Chatter.cs” are needed to create our test data.
Add Some Chat Controls
We’ll need three controls right away: a BulletedList to show all of the Chatters participating in the Chat, another BulletedList (part of the control toolkit) to display chat messages (from the server application), and a TextBox to enter new chat messages (from your client browser). You may wonder why we are using a BulletedList to display chat messages rather than a TextBox or ListBox control. It would certainly be possible to use either of those alternatives, however a BulletedList allows for more flexibility when styling the HTML output, and by setting the control’s CSS overflow style to auto we can create a ListBox-like experience pretty easily.
<asp:BulletedList ID="ChattersBulletedList" runat="server" />
<asp:BulletedList runat="server" ID="ChatMessageList" />
<asp:TextBox ID="NewMessageTextBox" Columns="50" runat="server" />
We’ll also add a button that we will later use to trigger an asynchronous call back to
Default.aspx:
<asp:Button ID="SendButton" Text="Send" runat="server" OnClick="SendMessage_Click"/>
The complete form should look something like this:
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true" />
Chatters<br/>
<asp:BulletedList ID="ChattersBulletedList" runat="server" />
Chat Text<br/>
<div style="width: 640px; height: 240px; overflow: auto;">
<asp:BulletedList runat="server" ID="ChatMessageList" />
</div>
Send Message Text<br/>
<asp:TextBox ID="NewMessageTextBox" Columns="50" runat="server" /><asp:Button ID="SendButton" Text="Send" runat="server" />
</form>
At this point you can run the project and check out the form.
Add Some Chatters
In a real-world scenario you would of course implement some kind of user authentication for your application. For the purposes of this example we will skip all of that and simply hard-code our chat application participants in the Application context. To do this, add a new Global.asax file to your project by right-clicking the project in the solution tree, clicking “Add New Item” and choosing Global Application Class.
Since we want to simulate a group of Chatters that have logged into our site, we will create a list of Chatter objects within the Application_Start method like so—
C#
List<Chatter> chatters = new List<Chatter>();
chatters.Add(new Chatter(new Guid("CD863C27-2CEE-45fd-A2E0-A69E62B816B9"), "Me"));
chatters.Add(new Chatter(Guid.NewGuid(), "Juan"));
chatters.Add(new Chatter(Guid.NewGuid(), "Joe"));
chatters.Add(new Chatter(Guid.NewGuid(), "Eric"));
chatters.Add(new Chatter(Guid.NewGuid(), "Brian"));
chatters.Add(new Chatter(Guid.NewGuid(), "Kim"));
chatters.Add(new Chatter(Guid.NewGuid(), "Victor"));
Application.Add("Chatters", chatters);VB
Dim chatters As List(Of Chatter) = New List(Of Chatter)
chatters.Add(New Chatter(New Guid("CD863C27-2CEE-45fd-A2E0-A69E62B816B9"), "Me"))
chatters.Add(New Chatter(Guid.NewGuid, "Juan"))
chatters.Add(New Chatter(Guid.NewGuid, "Joe"))
chatters.Add(New Chatter(Guid.NewGuid, "Eric"))
chatters.Add(New Chatter(Guid.NewGuid, "Brian"))
chatters.Add(New Chatter(Guid.NewGuid, "Kim"))
chatters.Add(New Chatter(Guid.NewGuid, "Victor"))
Application.Add("Chatters", chatters)
(Be sure to add <%@ Import Namespace="System.Collections.Generic" %> at the top of the file.) This is clearly test-only data, but the import thing to note is that each Chatter gets a new random Guid except for “Me” which gets a hard-coded value. In a real-world scenario this would probably be a user identification number stored in a cookie or Session variable, but since we are lazy, we will simply have hard-code it in two places to get a similar effect.
Next we will create a list of Chat’s available in the application (just one for now)—
List<Chat> chat = new List<Chat>();
chat.Add(new Chat());
Application.Add("Chats", chat);
Finally, we will loop through our list of logged-in users and associate them with the single Chat instance added above—
C#
foreach (KeyValuePair<Guid, Chatter> chatter in Chatter.ActiveChatters())
{
chatter.Value.Join(Chat.ActiveChats()[0]);
}
VB
For Each c As KeyValuePair(Of Guid, Chatter) In Chatter.ActiveChatters
c.Value.Join(Chat.ActiveChats(0))
Next
The Global.asax code should look something like this—
C#
void Application_Start(object sender, EventArgs e)
{
List<Chatter> chatters = new List<Chatter>();
chatters.Add(new Chatter(new Guid("CD863C27-2CEE-45fd-A2E0-A69E62B816B9"), "Me"));
chatters.Add(new Chatter(Guid.NewGuid(), "Juan"));
chatters.Add(new Chatter(Guid.NewGuid(), "Joe"));
chatters.Add(new Chatter(Guid.NewGuid(), "Eric"));
chatters.Add(new Chatter(Guid.NewGuid(), "Brian"));
chatters.Add(new Chatter(Guid.NewGuid(), "Kim"));
chatters.Add(new Chatter(Guid.NewGuid(), "Victor"));
Application.Add("Chatters", chatters);
List<Chat> chats = new List<Chat>();
chats.Add(new Chat());
Application.Add("Chats", chats);
foreach (KeyValuePair<Guid, Chatter> chatter in Chatter.ActiveChatters())
{
chatter.Value.Join(Chat.ActiveChats()[0]);
}
}VB
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
Dim chatters As List(Of Chatter) = New List(Of Chatter)
chatters.Add(New Chatter(New Guid("CD863C27-2CEE-45fd-A2E0-A69E62B816B9"), "Me"))
chatters.Add(New Chatter(Guid.NewGuid, "Juan"))
chatters.Add(New Chatter(Guid.NewGuid, "Joe"))
chatters.Add(New Chatter(Guid.NewGuid, "Eric"))
chatters.Add(New Chatter(Guid.NewGuid, "Brian"))
chatters.Add(New Chatter(Guid.NewGuid, "Kim"))
chatters.Add(New Chatter(Guid.NewGuid, "Victor"))
Application.Add("Chatters", chatters)
Dim chats As List(Of Chat) = New List(Of Chat)
chats.Add(New Chat)
Application.Add("Chats", chats)
For Each c As KeyValuePair(Of Guid, Chatter) In Chatter.ActiveChatters
c.Value.Join(Chat.ActiveChats(0))
Next
End Sub
When the test server first runs our project the code path above will be invoked and we will have a magical list of users in the Application context ready to chat away.
Handle Some Events
Open the code-behind file for Default.aspx (Default.aspx.cs) and add two private variables: one for our Chatter (“Me”), and one for the single Chat instance—
// You’ll notice these are the same values hard-coded in the Global.asax file as well
private Chat m_chat = Chat.ActiveChats()[0];
private Chatter m_chatter = Chatter.ActiveChatters()[new Guid("CD863C27-2CEE-45fd-A2E0-A69E62B816B9")];
Next, add a private method called _UpdateChatterList—
private void _UpdateChatterList()
{
ChattersBulletedList.DataSource = m_chat.Chatters;
ChattersBulletedList.DataTextField = "Name";
ChattersBulletedList.DataBind();
}
Since Chatters will enter and leave the Chat at any time, our global list of Chatters will need to update often. _UpdateChatterList binds our global list of Chatters to the appropriate BulletedList in the Web form every time we post-back to this page. We’ll also need to update and display the list of messages from our Chat instance. To do this we’ll add a new method called _UpdateChatMessageList—
C#
private void _UpdateChatMessageList()
{
ChatMessageList.DataSource = m_chat.Messages;
ChatMessageList.DataBind();
}VB
Private Sub _UpdateChatMessageList()
ChatMessageList.DataSource = m_chat.Messages
ChatMessageList.DataBind()
End Sub
Any messages that have been pushed to our Chat instance since the last post-back will now be appended to our Chat message BulletedList.
The last thing we need to do is handle the button click that occurs when a Chatter sends a new message to the Chat instance. To do this add an OnClick attribute to the “Send” button in Default.aspx—
<asp:Button ID="SendButton" Text="Send" runat="server" OnClick="SendButton_Click" />
Then, add the event handler for the button click to the code-behind. We’ll also call our “Update” methods after sending our chat message—
C#
protected void SendButton_Click(object sender, EventArgs e)
{
if (!string.IsNullOrEmpty(NewMessageTextBox.Text))
{
string messageSent = m_chat.SendMessage(m_chatter, NewMessageTextBox.Text);
}
_UpdateChatterList();
_UpdateChatMessageList();
}VB
Protected Sub SendButton_Click(ByVal sender As Object, ByVal e As EventArgs)
If Not String.IsNullOrEmpty(NewMessageTextBox.Text) Then
Dim messageSent As String = m_chat.SendMessage(m_chatter, NewMessageTextBox.Text)
NewMessageTextBox.Text = String.Empty
End If
_UpdateChatterList()
_UpdateChatMessageList()
End Sub
This will handle the click event from the “Send” button, check if we have any text to send, then call our Chat’s SendMessage method which will add the message to our global Chat object.
Try It Out!
Try running the project and sending a few messages. The page will do a complete post-back to the server every time the “Send” button is clicked. Lame. Let’s get rid of that by using the ASP.NET AJAX UpdatePanel. Download the sample project.
UpdatePanel: The Key to ASP.NET Async Happiness
Recall that in the first step we added the EnablePartialRendering attribute to the ScriptManager control. Now we can actually use that functionality by wrapping our Chat controls in an UpdatePanel. In the Default.aspx file encapsulate the BulletedList controls with an UpdatePanel as follows—
<asp:UpdatePanel ID="ChatUpdatePanel" runat="server" UpdateMode="Always">
<ContentTemplate>
Chatters<br/>
<asp:BulletedList ID="ChattersBulletedList" runat="server" />
Chat Text<br/>
<div style="width: 640px; height: 240px; overflow: auto;">
<asp:BulletedList runat="server" ID="ChatMessageList" />
</div>
</ContentTemplate>
</asp:UpdatePanel>
(Note that you must add a ContentTemplate element to the UpdatePanel before adding the Web controls.)
To circumvent the normal post-back processing we need to associate this UpdatePanel with a “Trigger,” so after the close of the ContentTemplate element add the following—
<Triggers>
<asp:AsyncPostBackTrigger ControlID="SendButton" EventName="Click" />
</Triggers>
Now “Send” button clicks will be handled asynchronously, and we don’t have to modify any of the synchronous logic we implemented above. Pretty nice!
One Last Trigger
Our chat application doesn’t quite work as we’d expect it to yet. We really want the list of messages to update continuously as new messages are sent from other Chatters. We can implement this by adding a new Trigger to the Triggers element, and wiring the new Trigger to a Timer control—
...
<asp:AsyncPostBackTrigger ControlID="ChatTextTimer" EventName="Tick" />
</Triggers>
Then, add a Timer control to the page. The Interval attribute determines the amount of time between “ticks” of the timer (in milliseconds)—
<asp:Timer runat="server" ID="ChatTextTimer" Interval="1000" />
Finally, add calls to UpdateChatterList and UpdateChatMessageList in the Page_Load event handler. When the timer Tick event fires the page will post-back, the Page_Load handler will be invoked and our message list and Chatter list will update as expected. With these changes in place go ahead and run the project again. You may notice that the message list resets its scroll position to the top every time it updates. To fix this add the following JavaScript block after the closing form tag and before the closing body tag in Default.aspx—
<script type="text/javascript">
function _SetChatTextScrollPosition()
{
var chatText = document.getElementById("ChatText");
chatText.scrollTop = chatText.scrollHeight;
window.setTimeout("_SetChatTextScrollPosition()", 1);
}
window.onload = function()
{
_SetChatTextScrollPosition();
}
</script>
The list will flicker slightly as it repositions itself, but the latest message will stay in view at the bottom of the list.
To test the functionality of your new chat application connect to the test URL with two separate browser windows and type messages into one or the other. You should see your messages pop into the message list of each one no matter which browser sends the message. Enjoy!
Download
Download the sample project.
Bio
“Tyler Schlegel is a Software Development Engineer with Personify Design, Inc. He’s been developing .NET solutions since 2002.”