Remote Scripting.Net (AJAX, NOT!) Zipcode
UserControl with MSN Virtual Earth Map

by Peter A. Bromberg, Ph.D.

Peter Bromberg

Ah, the trials and tribulations of a professional developer! No sooner do I get used to LongHorn than now its Vista. And of course, Remote Scripting somehow became "AJAX". Buzzwords abound, don't they? Hey, I'm not alone in my disdain for the highly unprofessional technique of misappropriation of a technique or technology for gain when nothing really new has been added. Some people (aka Tobin Titus) got so annoyed, they started a site to enlighten people.



So no matter what you call it, when you visit here you can rely on one thing: consistency! It was called Remote Scripting in 1998 when Microsoft brought it out, and it shall remain Remote Scripting here! In my last most recent article covering this subject I previewed Jason Diamond's excellent "Ajax.Net Library". We will use this again here, to create an ASCX ASP.NET UserControl that can be dragged onto any page. The only difference you'll see from Jason's original code is that the namespace has been changed back to RemoteScripting so as not to support the practice of Revisionist History!

The UserControl will query a database of US zipcodes when you enter any valid US 5 digit zipcode, and perform an out - of - band Remote Scripting callback to the database and update the page with the City, State, County, State and County US Gov't "FIPS" codes, and the Latitude and Longitude. But Wait! If you call in the the next 10 minutes, we'll even use those to query MSN Virtual Earth and return a map!

The nice thing about Jason Diamond's interpretation of .NET Remote Scripting is that his library is compact enough to include in the codebehind class for an ASCX UserControl, as I'll demonstrate:

using System;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;
using System.Data.SqlClient ;
using System.Collections;
using System.Reflection;
using System.Text;
using System.Web.UI;
namespace RemoteScripting
{
 
 public class ZipCodeControl : System.Web.UI.UserControl
 {
  protected System.Web.UI.WebControls.TextBox txtZip;
  protected System.Web.UI.WebControls.TextBox txtCity;
  protected System.Web.UI.WebControls.TextBox txtState;
  protected System.Web.UI.WebControls.TextBox txtCounty;
  protected System.Web.UI.WebControls.TextBox txtSTFIPS;
  protected System.Web.UI.WebControls.TextBox txtCTYFIPS;
  protected System.Web.UI.WebControls.TextBox txtLatitude;
  protected System.Web.UI.WebControls.Label lblZipInfo;
  protected System.Web.UI.WebControls.Panel Panel1;
  protected System.Web.UI.WebControls.TextBox txtLongitude;

  private void Page_Load(object sender, System.EventArgs e)
  {
   Page.RegisterStartupScript("ctrlId","<script>var ctrlId='"+this.ClientID +"_';</script>");
  }


  [RemoteScripting.Method]
  public DataTable  PopulateZipData(string zipCode)
  {
   SqlConnection cn = 
    new SqlConnection(System.Configuration.ConfigurationSettings.AppSettings["connString"].ToString());
   SqlCommand cmd = new SqlCommand("dbo.usp_GetZipCodeData",cn);
   cmd.CommandType =CommandType.StoredProcedure ;
   SqlParameter p = new SqlParameter("@Zipcode",this.txtZip.Text);
   cmd.Parameters.Add(p);
   SqlDataAdapter da = new SqlDataAdapter(cmd);
   DataSet ds = new DataSet();
   da.Fill(ds);
   DataTable tbl = ds.Tables[0];
   return tbl;   
  }

  #region Web Form Designer generated code
  override protected void OnInit(EventArgs e)
  {
   //
   // CODEGEN: This call is required by the ASP.NET Web Form Designer.
   //
   InitializeComponent();
   base.OnInit(e);
   RemoteScripting.Manager.Register(this,"ZipCode.Control",RemoteScripting.Debug.None);

  }
  
  /// <summary>
  ///  Required method for Designer support - do not modify
  ///  the contents of this method with the code editor.
  /// </summary>
  private void InitializeComponent()
  {
   this.Load += new System.EventHandler(this.Page_Load);

  }
  #endregion
 }

// Jason Diamond's enum and class here---
  
}

Now after you have digested the above, look at the ASCX (HTML) Portion:

<%@ Control Language="c#" AutoEventWireup="false" Codebehind="ZipCodeControl.ascx.cs" 
Inherits="RemoteScripting.ZipCodeControl" 
TargetSchema="http://schemas.microsoft.com/intellisense/ie5"%>
<script src="MapControl.js"></script>
<script>         
  
  function PopulateWithDataTable(elem) {
   if(elem.value.length <5) return; // need 5 digit zip code dood.   
   // show the user a message while we make the out-of-band call...
    document.getElementById(ctrlId+"lblZipInfo").innerText = "Getting Data...";         
   ZipCode.Control.PopulateZipData(elem.value,DoTableCallBack);    
  } 
  
  function DoTableCallBack(result) {
  // here is our callback where we process our return data in the "result" object
  var table=result.value; // yup, it gave us a DataTable in script.
  // now we populate our elements, if the first one throws, we know we got no data.  
  try{
  document.getElementById(ctrlId+"txtCity").innerText=table.Rows[0].CITY; 
  }
  catch(e)
  {
   document.getElementById(ctrlId+"lblZipInfo").innerText = "Not Found."; 
    document.getElementById(ctrlId+"txtCity").innerText=""; 
   document.getElementById(ctrlId+"txtState").innerText=""; 
        document.getElementById(ctrlId+"txtCounty").innerText="";     
        document.getElementById(ctrlId+"txtSTFIPS").innerText="";
        document.getElementById(ctrlId+"txtCTYFIPS").innerText=""; 
        document.getElementById(ctrlId+"txtLatitude").innerText=""; 
        document.getElementById(ctrlId+"txtLongitude").innerText=""; 
   return;
  
  }
  // we are ok, continue populating our fields.
    
  document.getElementById(ctrlId+"txtState").innerText=table.Rows[0].STATE; 
        document.getElementById(ctrlId+"txtCounty").innerText=table.Rows[0].COUNTY;     
        document.getElementById(ctrlId+"txtSTFIPS").innerText=table.Rows[0].STATEFIPS;
        document.getElementById(ctrlId+"txtCTYFIPS").innerText=table.Rows[0].COUNTYFIPS; 
        document.getElementById(ctrlId+"txtLatitude").innerText=table.Rows[0].LATITUDE; 
        document.getElementById(ctrlId+"txtLongitude").innerText=table.Rows[0].LONGITUDE;
          
        /*
           VE_MapControl(Latitude,  Longitude, Zoom, MapStyle, PositionType, 
                Left, Top, Width, Height);
        */
            map = new VE_MapControl(table.Rows[0].LATITUDE, table.Rows[0].LONGITUDE, 12, 

              'r', "absolute", 10, 300, 700, 500);
              

          document.getElementById(ctrlId+"Panel1").appendChild(map.element);
         document.getElementById(ctrlId+"lblZipInfo").innerText = "Done.";   
        
 } 
</script>
<P>
 <TABLE id="Table1" cellSpacing="2" cellPadding="2" width="300" border="2" title="Zip Code Data"
  borderColor="#ffffff">
  <TR bgColor="azure">
   <TD style="WIDTH: 102px; COLOR: black; FONT-FAMILY: Arial; HEIGHT: 27px" 
bgColor="azure">Zipcode</TD> <TD style="HEIGHT: 27px"> <asp:TextBox id="txtZip" runat="server" onblur="PopulateWithDataTable(this);">
</asp:TextBox><FONT face="Arial"></FONT></TD> </TR> <TR bgColor="azure"> <TD style="WIDTH: 102px"><FONT face="Arial">City</FONT></TD> <TD> <asp:TextBox id="txtCity" runat="server"></asp:TextBox><FONT face="Arial"></FONT></TD> </TR> <TR bgColor="azure"> <TD style="WIDTH: 102px"><FONT face="Arial">State</FONT></TD> <TD> <asp:TextBox id="txtState" runat="server"></asp:TextBox><FONT face="Arial"></FONT></TD> </TR> <TR bgColor="azure"> <TD style="WIDTH: 102px"><FONT face="Arial">County</FONT></TD> <TD> <asp:TextBox id="txtCounty" runat="server"></asp:TextBox><FONT face="Arial"></FONT></TD> </TR> <TR bgColor="azure"> <TD style="WIDTH: 102px"><FONT face="Arial">State FIPS</FONT></TD> <TD> <asp:TextBox id="txtSTFIPS" runat="server"></asp:TextBox><FONT face="Arial"></FONT></TD> </TR> <TR bgColor="azure"> <TD style="WIDTH: 102px"><FONT face="Arial">County FIPS</FONT></TD> <TD> <asp:TextBox id="txtCTYFIPS" runat="server"></asp:TextBox><FONT face="Arial"></FONT></TD> </TR> <TR bgColor="azure"> <TD style="WIDTH: 102px"><FONT face="Arial">Latitude</FONT></TD> <TD> <asp:TextBox id="txtLatitude" runat="server"></asp:TextBox><FONT face="Arial"></FONT></TD> </TR> <TR bgColor="azure"> <TD style="WIDTH: 102px"><FONT face="Arial">Longitude</FONT></TD> <TD> <asp:TextBox id="txtLongitude" runat="server"></asp:TextBox></TD> </TR> <TR bgColor="azure"> <TD style="WIDTH: 102px"></TD> <TD> <asp:Label id="lblZipInfo" runat="server" Width="152px" Font-Names="Arial"></asp:Label></TD> </TR> </TABLE> </P> <P>&nbsp;</P> <P>&nbsp;</P> <P> <asp:Panel id="Panel1" runat="server" Height="344px" style="POSTION:relative"></asp:Panel></P>

You can see there is a bunch of custom-written Javascript there. In ASP.NET 2.0 and beyond, with the ATLAS framework that will be previewed at the PDC in September 2005, you'll see some examples of "not having to" do this. Note the call to the Virtual Earth API, which is 100% javascript and utterly simple, as soon as the latitute and longitude have been returned from the remote scripting call.

And now, of course, you'll want to try it all out online, here: The downloadable solution below includes a SQL Script to generate the entire 42,000+ record Zipcode table! Enjoy. And remember, "AJAX" isn't anything but some lousy marketer's buzzword for Remote Scripting! Period! They didn't add anything new except perhaps some money to line their pockets with.

Download the complete VS.NET 2003 solution below

 

 


Peter Bromberg is a C# MVP, MCP, and .NET consultant who has worked in the banking and financial industry for 20 years. He has architected and developed web - based corporate distributed application solutions since 1995, and focuses exclusively on the .NET Platform.

Article Discussion: