Click here to Skip to main content
15,860,972 members
Articles / Web Development / HTML

Atlas Tutorial: Creating an AJAX Scribble Application

Rate me:
Please Sign up or sign in to vote.
4.86/5 (56 votes)
5 Feb 2006CPOL14 min read 449.6K   1.5K   278   86
A tutorial on creating an AJAX version of the popular MFC sample. The tutorial utilizes ASP.NET Atlas framework.

Please read instructions at the end of the article on how to run the application after downloading the source.

Image in Internet Explorer Image in FireFox

Introduction

ASP.NET Atlas is a rich set of client side and server side libraries to develop AJAX-style applications using ASP.NET. This tutorial (and probably more in this series) attempts to provide a general view of the features available in Atlas. Since, Atlas is a very vast library, this very first tutorial concentrates on two most important features of Atlas:

  1. Ability to call server side web services from client side scripts
  2. Ease of developing cross-browser compatible JavaScript code

Background

MFC Scribble application was one of the first applications that I used to learn MFC. Therefore, I decided to base this tutorial on Scribble. Scribble application allows users to draw freehand sketches using the mouse. I first saw a similar application on the web, utilizing AJAX technologies, at the JavaScript Draw website. The JavaScript draw website works only on Mozilla Firefox. This article describes how to build a cross-browser version of the application. We will build on the application in each article in this series to demonstrates more features of Atlas.

Installing Atlas

At the time of writing of this article, December CTP of Atlas can be downloaded by clicking this link. If this link does not work, you can always go to the Atlas website to get the correct link. The Atlas library is available as a Visual Studio 2005 template (VSI). The download site has instructions on how to install the template.

Creating an Atlas Project

Once the Atlas template is installed, you can create a blank Atlas project by clicking selecting the menu option: File -> New -> Web Site. This brings up the New Web Site dialog box as shown:

Visual Studio Project

Under location, you can either select File System or HTTP. Selecting HTTP will allow you to create a web site on an IIS server and selecting File System will allow you to create a web site on your local file system (which you can debug and test using the ASP.NET development web server). You can select either option, but I have found the application to work better with Internet Explorer on IIS.

Atlas Blank Project

The newly created Atlas web site has the following directory structure:

  • App_Data
    This is an empty directory where you can place data files.
  • Bin
    This is the directory where DLL file for the assembly Microsoft.Web.Atlas is placed. This contains the server portion of the Atlas library.
  • ScriptLibrary
    A directory where you can place any JavaScript files for the application.
    • Atlas
      Atlas client Scripts are placed here in two different subdirectories.
      • Debug
        The debug version of Atlas client side JavaScript files are placed in this directory.
      • Release
        The release version of Atlas client side JavaScript files are placed in this directory. The scripts in this directory are more compactly written and have some debug code removed.

Atlas Client Scripts

The December release of Atlas has the following client scripts:

  • Atlas.js
    This is the core Atlas script file consisting of basic utility functions and client side controls and components.
  • AtlasCompat.js
    This file contains the Atlas compatibility layer for supporting Mozilla Firefox and Apple-iMac-Safari web browsers. This script ensures that Atlas code is cross browser compatible.
  • AtlasCompat2.js
    Additional functions to ensure compatibility for the Safari web browser, are included in this file.
  • AtlasRuntime.js
    This is a scaled down version of the core Atlas script file. This script file does not have the client side components and controls. This script file can be used when the aforementioned components or controls are not being used in a web page.
  • AtlasUIDragDrop.js
    This file contains utility functions to provide drag drop functionality in a web page.
  • AtlasUIGlitz.js
    This file contains utility functions to provide animation and other special effects in a web page.
  • AtlasUIMap.js
    This is the script file for Atlas mapping framework that uses Virtual Earth.

Other Files

Atlas adds the following files to the root directory of the web site.

  • Default.aspx and Default.aspx.cs
    This is a web page containing Atlas Script Manager control that is responsible for rendering script blocks referring to the Atlas client side scripts. An client script of type test/xml-script block is also added to the page. This script block is used to write scripts using declarative XML syntax.
  • eula.rtf
  • readme.txt
  • Web.Config
    The web.config is essential for running Atlas applications. It contains some configuration settings specific to Atlas and also adds the Atlas HTTP modules and HTTP handlers.

The Scribble Application

The Scribble application allows users to draw freehand sketches by clicking on the left mouse button and moving mouse around. A sketch stroke ends when the user releases the mouse button or moves outside the drawing area. There are ways to draw using JavaScript by utilizing VML, but we are not going to use VML in this sample.

The default web page in Scribble will have an image (a regular HTML image - the IMG tag). The user mouse events over the image are captured using JavaScript event handlers. The JavaScript functions send the series of points in a sketch stroke to a web service. The web service updates and image object saved in a session variable by drawing lines through all the points sent by the client. Finally, the client requests an updated image from the server. The image source is an HTTP handler which streams the image stored in the session variable to the client. Here are the main components of the application.

  • Default.aspx
    The page with the dynamic image and Atlas Script Manager control.
  • ScribbleImage.ashx
    This is an HTTP handler which streams the image object stored in the session variable.
  • ScribbleService.asmx
    This is the web service to which all the sketching requests are sent. The webservice modifies the image.
  • Scribble.js
    The JavaScript code for the application resides in this file to make a clear separation between design and the code.
  • Global.asax
    The Session_Start and the Session_End events are handled in Global.asax. The Session_Start creates the session variable and Session_End disposes the image stored in the session variable.

Global.asax

We begin our coding process from Global.asax.

  1. In the Website menu, click on Add New Item or press Ctrl + Shift + A.
  2. In the Add New Item dialog box, select Global Application Class and click ok. You will see the Global.asax file created.
  3. We start by importing the System.Drawing namespace. Insert the following line of code just after the first line:
    ASP.NET
    <%@ Import Namespace="System.Drawing" %>        
  4. Add the following code to Session_Start function:
    C#
    void Session_Start(object sender, EventArgs e)
    {
        Bitmap bmp = new Bitmap(200, 200);
        using (Graphics g = Graphics.FromImage(bmp))
        {
            g.FillRectangle(new SolidBrush(Color.White),
                new Rectangle(0, 0, bmp.Width, bmp.Height));
            g.Flush();
        }
    
        Session["Image"] = bmp;
    }        
    The code creates a simple bitmap 200 pixels by 200 pixels white, paints the entire background white and assigns it to the session variable named Image.
  5. The Session_End function should dispose the image stored in the session variable.
    C#
    Bitmap bmp = (Bitmap)Session["Image"];
    bmp.Dispose();
    
  6. From the Website menu, select Add Reference.
  7. Select System.Drawing in the Add Reference dialog box and click OK.
  8. Finally, in the Build menu click Build Web Site or press Ctrl + Shift + B to make sure that there are no build errors.

ScribbleImage.ashx

This web handler is supposed to stream the image stored in the session variable back to the client.

  1. In the WebSite menu, click on Add New Item or press Ctrl + Shift + A.
  2. In the Add New Item dialog box, select Generic Handler, set the name of the handler to ScribbleImage.ashx and click OK.
  3. For a web handler to use session variables, it needs to implement the interface IRequiresSessionState. This is only marker interface and has no methods to override. Edit the class declaration to look like the following:
    C#
    public class ScribbleImage : IHttpHandler,
        System.Web.SessionState.IRequiresSessionState
  4. Next, we add code to the ProcessRequest method.
    C#
    public void ProcessRequest (HttpContext context) 
    {
        context.Response.ContentType = "image/png";
        context.Response.Cache.SetNoStore();
        context.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        context.Response.Cache.SetExpires(DateTime.Now);
        context.Response.Cache.SetValidUntilExpires(false);
        
        System.Drawing.Bitmap bmp =
            (System.Drawing.Bitmap)context.Session["Image"];
    
        lock(bmp)
        {
            using (MemoryStream ms = new MemoryStream())
            {
                bmp.Save(ms, ImageFormat.Png);
                ms.Flush();
                context.Response.BinaryWrite(ms.GetBuffer());
            }
        }
        }
    } 
    • The first line sets ContentType header in the response to image/png. This make sure that the browser identifies the response to be a png image instead of HTML.
    • The next four lines indicate to the browser that the response should not be cached. All these four lines are necessary to make sure that the code is cross browser compatible. We will optimize the code in the later versions of the tutorial.
    • Finally, the bitmap from the session variable is saved to a memory stream and the contents of the memory stream are written to response. The BinaryWrite function is used, as the image is binary data.

ScribbleService.asmx

We have means of initializing the session image and to stream the image contents as response. Now, we need some method to add content to the image itself. We expect the clients to call on ScribbleService.asmx web service to add lines to the image.

  1. In the WebSite menu, click on Add New Item or press Ctrl + Shift + A.
  2. In the Add New Item dialog box, select Web Service, specify the name to be ScribbleService.asmx and click OK. Make sure that you uncheck Place Code in a Separate File.
  3. Import the namespace System.Drawing by adding the following line to the series of namespace imports:
    C#
    using System.Drawing;
  4. Next, we need to define a simple class for a point. We cannot use the System.Drawing.Point class as it is not XML serializable. In a later tutorial, we will see how we can use System.Drawing.Point instead of the custom class. Add the following code just before the ScribbleService class declaration:
    C#
    public class Point
    {
       public int X;
       public int Y;
    };    
  5. Finally, we need to add a method to draw the sketch given a set of points. We add a web method Draw to our web service.
    C#
    [WebMethod(EnableSession = true)]
    public void Draw(Point[] points)
    {
        Image scribbleImage = (Image)Session["Image"];
        lock(scribbleImage)
        {
            using (Graphics g = Graphics.FromImage(scribbleImage))
            using(Pen p = new Pen(Color.Black, 2))
            {
                if (points.Length > 1)
                {
                    int startX = points[0].X;
                    int startY = points[0].Y;
    
                    for (long i = 1; i < points.Length; i++)
                    {
                        g.DrawLine(p, startX, startY,
                            points[i].X, points[i].Y);
                        startX = points[i].X;
                        startY = points[i].Y;
                    }
                }
            }
        }
    } 
    • The attribute WebMethod(EnableSession = true) ensures that the session variables are accessible from the web service.
    • The image is locked to make sure that concurrent accesses are safe.
    • The drawing itself is pretty simple as it just joins the points supplied in the points array.

Scribble.js

We have the server side image handler and the server side web service to update the image. Now, we need the client side script in scribble application that will send the points from mouse events to the server web service.

  1. Highlight the ScriptLibrary folder in the solution explorer.
  2. In the WebSite menu, click on Add New Item or press Ctrl + Shift + A.
  3. In the Add New Item dialog box, select JScript File, sepcify the name to be Scribble.js and click OK. This will place Scribble.js in the ScriptLibrary folder.
  4. Next, we need to declare some global variables. In this first version of scribble, we will use global variables but in the coming versions, we start using JavaScript objects.
    JavaScript
    //The HTML image element that is to be drawn
    var image;
    //The source of the image
    var originalSrc;
    //The number of iteration
    var iter = 0;
    //The array of points
    var points = null;
    The comments above the variable declaration describe the purpose behind each variable. The iter variable is used to modify the source of the image after a draw request is sent to the server. In case of Internet Explorer, setting image.src = image.src does refresh the image but the same code does not work for Firefox. To work around this problem, we maintain the variables iter which is incremented every time we send a Draw request to the Webservice. We add the iteration number to the originalSrc variable so that the browser thinks that it needs to request the server to get fresh data as opposed to using the cached image.
  5. We define the function startStroke which starts a stroke in response to the mousedown event.
    JavaScript
    function startStroke()
    {
        points = new Array();
        window.event.returnValue = false;
    }
    When a new stroke starts, we create a fresh set of points as indicated by the first line. The second line cancels the default behavior of the event. This is necessary as the default behavior of the mousedown event for an image is to start a drag operation which prevents any further events from being fired.
  6. When a stroke ends in response to the mouseup event or mouseout event, we need to make the actual call to the webservice. This is done in the endStroke function.
    JavaScript
    function endStroke()
    {
        if (!points || points.length < 2)
            return true;
    
        //Send the points to the webservice
        ScribbleService.Draw(points, onWebMethodComplete,
            onWebMethodTimeout, onWebMethodError);
        points = null;
        window.event.returnValue = false;
    }
    The only interesting line in the function is ScribbleService.Draw(points, onWebMethodComplete, onWebMethodTimeout, onWebMethodError); , which invokes the web service method Draw in ScribbleService.asmx asynchronously. The function is automatically made available to us by the Atlas framework.
  7. onWebMethodError is a function which gets invoked by Atlas framework when an error occurs in the web service method and onWebMethodTimeout gets invoked when the web method call exceeds a configurable timeout defined in the Atlas framework. In this version, we just show the user a message box with the error text.
    JavaScript
    function onWebMethodError(fault)
    {
        alert("Error occured:\n" + fault.get_message());
    }
    
    function onWebMethodTimeout()
    {
        alert("Timeout occured");
    }
  8. onWebMethodComplete function is called when the web method call is successful. The image needs to be reloaded when this happens.
    JavaScript
    function onWebMethodComplete(result, response, context)
    {
        //We need to refresh the image
        var shimImage = new Image(200, 200);
        shimImage.src = originalSrc + "?" + iter++;
        shimImage.onload = function()
        {
            image.src = shimImage.src;
        }
    }
    We create an Image object shimImage and set its source to the original source of the image we are drawing on. When the image object loads, we set the source of the actual HTML image element on the page to the source of the temporary image object. This is done to avoid flicker when replacing the image.
  9. We need to fill the points array during the mousemove event. This is done in the addPoints function.
    JavaScript
    function addPoints()
    {
        if (points)
        {
            var point = { X : window.event.offsetX,
                Y : window.event.offsetY};
            points.push(point);
            
            if (points.length == 3)
            {
                endStroke();
                points = new Array();    
                points.push(point);
            }
    
            window.event.returnValue = false;
        }
    }
    • A new point object is constructed with the offsetX and offsetY properties of the event object and then it is appended to the points array. The offsetX and offsetY properties give the relative mouse position with respect to the HTML element causing the event.
    • If the length of array has reached 3, we automatically request the server to do a draw operation and reset the points array. This is done so that the user can see the drawing before he releases the mouse button.
  10. Finally, we need to hook the events, this is done in the pageLoad function.
    JavaScript
    function pageLoad()
    {
        var surface = document.getElementById("drawingSurface");
        image = surface.getElementsByTagName("IMG")[0];
        originalSrc = image.src;
    
        surface.attachEvent("onmousedown", startStroke);
        surface.attachEvent("onmouseup", endStroke);
        surface.attachEvent("onmouseout", endStroke);
        surface.attachEvent("onmousemove", addPoints);
    }
    • The pageLoad function is a special function which gets invoked when the Atlas framework has finished loading. We use this instead of the regular window or body load events so that we can be sure that Atlas has finished loading.
    • The actual image element which gets sketched is placed, inside a div tag with an id of drawingSurface. The size of the element is same as the size of the image so we can safely attach to the events to the drawingSurface div.

Default.aspx

The individual components of the application need to be assembled in the Default.aspx page. Here is the code of this page.

ASP.NET
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs"
    Inherits="_Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
    "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Atlas Scribble Sample</title>
</head>
<body>
    <form id="form1" runat="server">
        <Atlas:ScriptManager ID="AtlasScriptManager" runat="server"
            EnableScriptComponents="False" >
    <Services>
    <Atlas:ServiceReference Path="ScribbleService.asmx" />
    </Services>
    <Scripts>
    <Atlas:ScriptReference Path="ScriptLibrary/Scribble.js" />
    </Scripts>
    </Atlas:ScriptManager>
        <div id="drawingSurface"
            style="border:solid 1px black;height:200px;width:200px">
        <img alt="Scribble" src="ScribbleImage.ashx"
            style="height:200px;width:200px" galleryimg="false" />
        </div>
    </form>
</body>
</html>

The most important aspect of this page is the atlas:ScriptManager server control. The ScriptManager server control is responsible for generating client side script blocks for Atlas and for any web service proxy scripts. Let's examine the usage of the ScriptManager control in the Default.aspx page.

  1. The EnableScriptComponents property is set to false. This generates a client side script block referring to AtlasRuntime.js instead of Atlas.js. We prefer the lightweight version of Atlas Framework in this version of scribble as we are not using any Atlas components or controls.
  2. We add a service reference to the ScribbleService.asmx web service. This will generate a URL reference to client side script for the web service proxy.
  3. We add the Scribble.js as another script reference.

This brings all the pieces together and now you can compile and run the project. I encourage you to see the actual client HTML generated by the Atlas Script Manager.

The Atlas Magic

Here is what Atlas framework did for us:

  1. It allowed us to write the web application without us making special efforts to make it cross browser. The web service invocation and the client side event handling automatically works on both Internet Explorer and Firefox. The Atlas framework adds the required JavaScript prototypes to Firefox objects to make them look like Internet Explorer objects. The Internet Explorer specific functions like attachEvent and event.offsetX and event.offsetY are made available to Firefox. You can look at AtlasCompat.js file to see how this is done.
  2. It automatically created a JavaScript proxy for the Scribble web service methods. The JavaScript proxy script file for a ScribbleService.asmx file has the URL ScribbleService.asmx/js. This is generated by the Atlas HTTP module added in the web.config.

Where are We

We have seen how to call web services and how to write cross browser applications with ease using Atlas. In an upcoming tutorial, we see more of Atlas client side controls and declarative programming (depending on user feedback!). If you liked the tutorial, please feel free to write a comment. If you did not like it, please write a comment on how to improve it.

Downloading and Running the Source

As Atlas is not yet redistributable, I have not included the Atlas files in the source download. Here are the steps you need to get the downloaded source to work.

  1. You need to download Atlas from Atlas Web Site.
  2. After downloading the Atlas blank project template, create a new Web Site by pointing to New in the File menu and selecting Web Site
  3. Extract the source zip file to the directory of newly created projects overwriting any existing files.
  4. In the Website menu, select Add Existing Item, add ScribbleService.asmx and ScribbleImage.ashx from the root directory of the website, and Scribble.js from the ScriptLibrary folder.
  5. Build and run the web site.

License

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


Written By
Architect
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: Compile Errors on January Release Pin
Rama Krishna Vavilala1-Mar-06 3:26
Rama Krishna Vavilala1-Mar-06 3:26 
GeneralRe: Compile Errors on January Release Pin
Stuart Gygi4-Mar-06 9:04
Stuart Gygi4-Mar-06 9:04 
GeneralRe: Compile Errors on January Release Pin
Rama Krishna Vavilala4-Mar-06 10:58
Rama Krishna Vavilala4-Mar-06 10:58 
GeneralMemoryStream help Pin
steveIA27-Feb-06 14:16
steveIA27-Feb-06 14:16 
GeneralRe: MemoryStream help Pin
theBube28-Feb-06 0:28
theBube28-Feb-06 0:28 
GeneralMemoryStream help Pin
steveIA27-Feb-06 14:15
steveIA27-Feb-06 14:15 
QuestionNothing happens..... Pin
kyeongster23-Feb-06 13:01
kyeongster23-Feb-06 13:01 
AnswerRe: Nothing happens..... Pin
Rama Krishna Vavilala23-Feb-06 13:42
Rama Krishna Vavilala23-Feb-06 13:42 
QuestionRe: Nothing happens..... Pin
kyeongster24-Feb-06 4:05
kyeongster24-Feb-06 4:05 
AnswerRe: Nothing happens..... Pin
Rama Krishna Vavilala24-Feb-06 4:13
Rama Krishna Vavilala24-Feb-06 4:13 
QuestionRe: Nothing happens..... Pin
kyeongster24-Feb-06 5:18
kyeongster24-Feb-06 5:18 
AnswerRe: Nothing happens..... Pin
GLMnet28-Feb-06 16:30
GLMnet28-Feb-06 16:30 
QuestionRe: Nothing happens..... Pin
kyeongster1-Mar-06 4:43
kyeongster1-Mar-06 4:43 
GeneralConfiguration Error Pin
clinchgene13-Feb-06 19:40
clinchgene13-Feb-06 19:40 
GeneralRe: Configuration Error Pin
Rama Krishna Vavilala14-Feb-06 1:22
Rama Krishna Vavilala14-Feb-06 1:22 
GeneralGreat Tutorial.. but found one flaw. Pin
raedwa018-Feb-06 4:25
raedwa018-Feb-06 4:25 
GeneralRe: Great Tutorial.. but found one flaw. Pin
Rama Krishna Vavilala8-Feb-06 4:49
Rama Krishna Vavilala8-Feb-06 4:49 
GeneralA very nice web control for scribble Pin
qiuyl7-Feb-06 19:37
qiuyl7-Feb-06 19:37 
GeneralRe: A very nice web control for scribble Pin
Rama Krishna Vavilala8-Feb-06 3:35
Rama Krishna Vavilala8-Feb-06 3:35 
QuestionIt doesn't work... Pin
Jason Hsu6-Feb-06 18:07
Jason Hsu6-Feb-06 18:07 
AnswerRe: It doesn't work... Pin
Rama Krishna Vavilala7-Feb-06 1:44
Rama Krishna Vavilala7-Feb-06 1:44 
GeneralCool... Pin
matthew_evans31-Jan-06 19:56
matthew_evans31-Jan-06 19:56 
GeneralRe: Cool... Pin
Joshua Lunsford6-Feb-06 11:41
Joshua Lunsford6-Feb-06 11:41 
GeneralRe: Cool... Pin
qiuyl7-Feb-06 19:38
qiuyl7-Feb-06 19:38 
GeneralGreat article, but needs a small fix Pin
tyfud31-Jan-06 9:35
tyfud31-Jan-06 9:35 

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

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