Overview
The purpose of this article is to help beginners
who may have experience using the Virtual Earth API but have no knowledge of
how to move from using client side JavaScript to invoking server side backend using
AJAX and .ashx handlers.
Essentially, the AJAX portion is the use of the
XMLHttpRequest object which can communicate to the .ashx handler that in turn invokes
the server side logic.
This can be applied to many situations such as plotting
data from a SQL database, or leveraging the Mappoint Web Service to execute
additional functionality not capable by the current version of Virtual Earth.
In this article we will create a basic web page with an AJAX/.ashx handler
setup and then apply this to polling SQL results from a database.
The XMLHttpRequest Object
Start by creating a new web site called
HTMLPost (file -> new -> website -> asp.net web site click “OK”) and
add a new JavaScript file called AJAX.js (right click project -> add ->
new item -> Jscript file -> click “Add”) to your web site.

Figure
1 - Creating a Web Site


Figure
2 - Adding a JavaScript file
Inside our AJAX.js file, we will need to
initialize our XMLHttpRequest object.
When the request to the .ashx handler
has finished completed, a function XMLHttpRequestCompleted will be invoked.
Add the following code:
function InitXmlHttp() {
// Attempt to initialize xmlhttp object
try
{
xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e)
{
// Try to use different activex object
try
{
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (E)
{
xmlhttp = false;
}
}
// If not initialized, create XMLHttpRequest object
if (!xmlhttp && typeof XMLHttpRequest!='undefined')
{
xmlhttp = new XMLHttpRequest();
}
// Define function call for when Request obj state has changed
xmlhttp.onreadystatechange=XMLHttpRequestCompleted;
}
Listing
1 – Initializing the XMLHttpRequest object
Also, we will invoke an .ashx file called
FindNearby.ashx and set the asynchronous Boolean to true (to prevent hanging if
an error occurs in our .ashx handler.
function InvokeASHX()
{
InitXmlHttp();
xmlhttp.onreadystatechange= XMLHttpRequestCompleted;
xmlhttp.open("GET", "" + "Handler.ashx", true );
xmlhttp.send(null);
}
Listing
2 – Invoking the .ashx handler
Finally, we will add a callback function
XMLHttpRequestCompleted which will be invoked when our XMLHTTPRequest object
has finished communicating with the .ashx handler. Inside our callback
function we will execute a JavaScript alert to show our handler response.
function XMLHttpRequestCompleted()
{
if (xmlhttp.readyState==4)
{
try
{
alert(xmlhttp.responseText);
}
catch (e)
{
}
}
}
Listing
3 – Handling the response from the .ashx handler
The .ashx Handler
Now we will create our .ashx handler which
will handle take the AJAX request to execute our server side logic. Right
click your web project -> Add New Items -> Generic Handler -> Click
“Add”

Figure
3 – Adding the .ashx handler
The file generated is basically a code
behind file that implements the interface “IHttpHandler”. Inside the
ProcessRequest method we will handle our AJAX request (passed as an instance of
HttpContext known as “context” by default).
<%@ WebHandler Language="C#" Class="Handler" %>
using System;
using System.Web;
public class Handler : IHttpHandler {
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/plain";
context.Response.Write("Hello World");
}
public bool IsReusable {
get {
return false;
}
}
}
Listing
4 – The .ashx handler generated code
Finally, include a reference to the AJAX.js
file in the default.aspx page and call the InvokeASHX method.
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="HtmlPost.WebForm1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Untitled Page</title>
<script type="text/javascript" src="/Portals/0/AJAX.js"></script>
</head>
<body onload="InvokeAshx();">
<form id="form1" runat="server">
<div>
</div>
</form>
</body>
</html>
Listing
5 –Invoking AJAX.js on the default.aspx page
Combining Listing 1-5, you should now have
a web site that outputs “Hello World” to the screen.

Figure
4 - Invoking the .ashx handler
from client side
Hello World Virtual Earth
To apply everything so far to Virtual
Earth, begin by modifying the default.aspx file to include a Virtual Earth
map. We will call the InvokeASHX() method inline code for illustrative
purposes:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="HtmlPost.WebForm1" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script type="text/javascript" src="/Portals/0/AJAX.js"></script>
<script type="text/javascript" src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6"></script>
<script type="text/javascript">
var map = null;
function GetMap()
{
map = new VEMap('myMap');
map.LoadMap();
InvokeASHX();
}
</script>
</head>
<body onload="GetMap();">
<div id='myMap' style="position:relative; width:400px; height:400px;"></div>
</body>
</html>
Listing
6 – Setting up Virtual Earth
Inside our Handler.ashx file, adjust the
ProcessRequest method to the following:
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "text/plain";
context.Response.Write("var shape = new VEShape(VEShapeType.Pushpin, map.GetCenter());");
context.Response.Write("shape.SetTitle(\'Hello World!\');");
context.Response.Write("map.AddShape(shape);");
}
Listing
7 – Sending up Virtual Earth requests from the .ashx handler
Finally in the AJAX.js file, we will
evaluate the response from the handler rather than raising an alert:
function XMLHttpRequestCompleted()
{
if (xmlhttp.readyState==4)
{
try
{
eval(xmlhttp.responseText);
}
catch (e)
{
}
}
}
Listing
8 – Setting up AJAX.js to handle Virtual Earth requests

Figure
5 - Resulting VE page
Below this article
is a sample web site you can download and then compile that demonstrates this simple Virtual
Earth application.
Invoking a SQL Proximity Search inside the .ashx handler
Inside the ProcessRequest method of the
.ashx handler, we can execute our server side logic and then pass up the
content in an array of characters using the context.Response.Write method which
will be evaluated by our client side JavaScript.
One practical example is to
plot nearby locations from a SQL database onto our VE map. The .ashx code
below will execute a proximity search using SQL as outlined at http://msdn2.microsoft.com/en-us/library/ms980211.aspx
and generate the JavaScript to add shapes onto a VE map (assuming that the
database has been created as per the above MSDN article and that pushpin
information is being appropriately returned to the drLocations SqlDataReader).
public void ProcessRequest(HttpContext context)
{
string connectionString = appSettings.Get("ConnectionString");
StringBuilder response = new StringBuilder();
connection = new SqlConnection(connectionString);
try
{
connection.Open();
SqlCommand cmdLocations = new SqlCommand("sp_FindNearby", connection);
cmdLocations.CommandType = CommandType.StoredProcedure;
cmdLocations.Parameters.Add("@Centerlat", SqlDbType.Float);
cmdLocations.Parameters["@CenterLat"].Value = 45.21;
cmdLocations.Parameters.Add("@CenterLon", SqlDbType.Float);
cmdLocations.Parameters["@CenterLon"].Value = -71.5;
cmdLocations.Parameters.Add("@SearchDistance", SqlDbType.Float);
//Execute a FindNearby of 200 kilometers to the users entered location
cmdLocations.Parameters["@SearchDistance"].Value = 200;
//Using Earth Radius in Kilometers
cmdLocations.Parameters.Add("@EarthRadius", SqlDbType.Float);
cmdLocations.Parameters["@EarthRadius"].Value = 6378.1;
cmdLocations.Parameters.Add("@StoreType", SqlDbType.VarChar);
cmdLocations.Parameters["@StoreType"].Value = context.Request.Params.Get(0);
SqlDataReader drLocations;
drLocations = cmdLocations.ExecuteReader();
while (drLocations.Read())
{
response.Append(String.Format("datashape = new VEShape(VEShapeType.Pushpin, new VELatLong({0},{1}));", drLocations[1],drLocations[2]));
response.Append(String.Format("datashape.SetTitle('{0}');",drLocations[3]));
response.Append(String.Format("datashape.SetDescription('{0}');",drLocations[4]));
response.Append(String.Format("map.AddShape(datashape);"));
}
context.Response.Write(response.ToString());
}
catch (Exception ex)
{
context.Response.Write("alert(\"" + ex.ToString() + "\")");
}
connection.Close();
}
Listing
9 – Executing a Proximity Search and passing the results back to Virtual Earth
This will look for the nearest locations
200 kilometers from the (45.21,-71.5) co-ordinate inside the table defined by
the sp_FindNearby stored procedure.
We can make the following modifications to
our JavaScript code to pass parameters to our handler and make our solution
more robust:
-
Add parameters to our xmlhttp.open method such
that it looks like
xmlhttp.open("GET", "" + "Handler.ashx" + "?Latitude=40.21&Longitude=-71.5", true );
Listing
10 – Passing parameters to the .ashx handler
-
And then get the parameters by using the context.Request.Params.Get
method
cmdLocations.Parameters["@CenterLat"].Value = context.Request.Params.Get(0);
cmdLocations.Parameters["@CenterLon"].Value = context.Request.Params.Get(1);
Listing
11 – Processing the parameters through the .ashx handler
After applying these changes the proximity
search should now be executing from a co-ordinate of (40.21,-71.5).
Conclusion
Adding an intermediate layer to your VE
applications is essential in order to establish a bridge between client and
server side communication. In part 2, we will be looking at how to apply some
useful functions from the Mappoint Web Service, Microsoft’s older mapping
product onto a VE map.
Article contributed by Derek Chan (www.infusion.com). Have you got something
to contribute?