Archive for August, 2008|Monthly archive page

Google AJAX Language Custom Server Control

I tried Google AJAX Language API yesterday with a “Hello World” example, today I build an ASP.NET AJAX-enabled custom server control for it. The coding process is fun and it’s way simpler than building Virtual Earth Map AJAX-enabled controls at work. If you need to learn how to build an ASP.NET AJAX-enabled custom server control, this tutorial will help you.

My Google AJAX Language custom server control is straightforward and it extends System.Web.UI.WebControls.Label class and implements System.Web.UI.IScriptControl interface. I will list the source code and give some notes.

GoogleLanguageLabel.cs – server-side code of control

  1. Text property is overridden – this is the text will be translated to target language based on the LanguageCode property.
  2. LanguageCode property – See Google Language Enum for valid codes.
  3. AssemblyInfo.cs and WebResource – Add the following line to AssemblyInfo.cs because we will make the associated Javascript as web resource: [assembly: System.Web.UI.WebResource("Posts.AjaxControl.GoogleLanguageLabel.js", "application/x-javascript")]

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Text;

using System.Web;

using System.Web.UI;

using System.Web.UI.WebControls;

namespace Posts.AjaxControl

{

[DefaultProperty("Text")]

[ToolboxData("<{0}:GoogleLanguageLabel runat=server></{0}:GoogleLanguageLabel>")]

public class GoogleLanguageLabel : Label, IScriptControl

{

[Category("Appearance")]

public override string Text

{

get { return ((ViewState["Text"] != null) ? ViewState["Text"].ToString() : String.Empty); }

set { ViewState["Text"] = value; }

}

[Category("Appearance")]

public string LanguageCode

{

get { return ((ViewState["LanguageCode"] != null) ? ViewState["LanguageCode"].ToString() : “en”); }

set { ViewState["LanguageCode"] = value; }

}

protected virtual IEnumerable<ScriptReference> GetScriptReferences()

{

ScriptReference reference = new ScriptReference(Page.ClientScript.GetWebResourceUrl(typeof(GoogleLanguageLabel), “Posts.AjaxControl.GoogleLanguageLabel.js”));

return new ScriptReference[] { reference };

}

protected virtual IEnumerable<ScriptDescriptor> GetScriptDescriptors()

{

ScriptControlDescriptor descriptor = new ScriptControlDescriptor(“AjaxControl.GoogleLanguageLabel”, this.ClientID);

descriptor.AddProperty(“text”, this.Text);

descriptor.AddProperty(“languageCode”, this.LanguageCode);

return new ScriptDescriptor[] { descriptor };

}

IEnumerable<ScriptReference> IScriptControl.GetScriptReferences()

{

return GetScriptReferences();

}

IEnumerable<ScriptDescriptor> IScriptControl.GetScriptDescriptors()

{

return GetScriptDescriptors();

}

protected override void OnPreRender(EventArgs e)

{

if (!this.DesignMode)

{

// Test for ScriptManager and register if it exists

ScriptManager sm = ScriptManager.GetCurrent(Page);

if (sm == null)

{

throw new HttpException(“A ScriptManager control must exist on the current page.”);

}

sm.RegisterScriptControl(this);

}

base.OnPreRender(e);

}

protected override void Render(HtmlTextWriter writer)

{

if (!this.DesignMode)

{

ScriptManager.GetCurrent(Page).RegisterScriptDescriptors(this);

}

base.Render(writer);

}

}

}

GoogleLanguageLabel.js – client-side code of control

  1. Global variable – I define a global variable “global_google” to make sure it’s hooked up with the “google” object from the external Javascript http://www.google.com/jsapi, and this global variable can be accessed inside Javascript functions.
  2. Save context – Remember to save context (“this._languageCode” and “this.get_element()” in this case) before going inside the inner functions.

Type.registerNamespace(‘AjaxControl’);

var global_google = google;

AjaxControl.GoogleLanguageLabel = function(element)

{

AjaxControl.GoogleLanguageLabel.initializeBase(this, [element]);

this._text = null;

this._languageCode = null;

}

AjaxControl.GoogleLanguageLabel.prototype =

{

initialize : function() {

AjaxControl.GoogleLanguageLabel.callBaseMethod(this, ‘initialize’);

// save context

var langugaeCode = this._languageCode;

var element = this.get_element();

// current text

var text = this.get_element().innerHTML;

// detect and translate

global_google.language.detect(text, function(result) {

if (!result.error && result.language) {

global_google.language.translate(text, result.language, langugaeCode, function(result) {

if (result.translation) {

element.innerHTML = result.translation;

}

});

}

});

},

dispose : function() {

AjaxControl.GoogleLanguageLabel.callBaseMethod(this, ‘dispose’);

},

// *** Control properties ***

set_text : function(value) {

if (this._text !== value) {

this._text = value;

this.raisePropertyChanged(‘text’);

}

},

get_text : function() { return this._text; },

set_languageCode : function(value) {

if (this._languageCode !== value) {

this._languageCode = value;

this.raisePropertyChanged(‘languageCode’);

}

},

get_languageCode : function() { return this._languageCode; }

}

AjaxControl.GoogleLanguageLabel.registerClass(‘AjaxControl.GoogleLanguageLabel’, Sys.UI.Control);

if (typeof(Sys) !== ‘undefined’) Sys.Application.notifyScriptLoaded();

GoogleLanguageLabel.aspx – demo page

  1. Register web server control – <%@ Register Namespace=”Posts.AjaxControl” Assembly=”Posts” TagPrefix=”ac” %>
  2. Link to the external Javascript http://www.google.com/jsapi.
  3. Include Google AJAX Lauguage API in the page – call google.load(“language”, “1″); to load version 1 of the AJAX Language API.
  4. Create <ac:GoogleLanguageLabel> controls and assign Language Code.

<%@ Page Language=”C#” AutoEventWireup=”true” CodeBehind=”GoogleLanguageLabel.aspx.cs” Inherits=”Posts.AjaxControlDemo.GoogleLanguageLabel” %>

<%@ Register Namespace=”Posts.AjaxControl” Assembly=”Posts” TagPrefix=”ac” %>

<!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>Google Language Label</title>

<script type=”text/javascript” src=”http://www.google.com/jsapi”></script>

<script type=”text/javascript”>

google.load(“language”, “1″);

</script>

</head>

<body>

<form id=”form1″ runat=”server”>

<asp:ScriptManager ID=”ScriptManager1″ runat=”server” />

<div>

<ac:GoogleLanguageLabel ID=”lblMessage1″ runat=”server” Text=”Hello!” LanguageCode=”zh-TW” />

<br />

<ac:GoogleLanguageLabel ID=”lblMessage2″ runat=”server” Text=”How are you?” LanguageCode=”fr” />

<br />

<ac:GoogleLanguageLabel ID=”lblMessage3″ runat=”server” Text=”Can I borrow 100 dollars from you?” LanguageCode=”hi” />

<br />

<ac:GoogleLanguageLabel ID=”lblMessage4″ runat=”server” Text=”Can I return your money when I win the lottery?” LanguageCode=”ja” />

<br />

<ac:GoogleLanguageLabel ID=”lblMessage5″ runat=”server” Text=”Actually I just find I still have 1000 dollars in my wallet.” LanguageCode=”ru” />

</div>

</form>

</body>

</html>

Here is the result:

Hello!
Comment vas-tu?
उधार लेने से 100 डॉलर कर सकता हूँ ?
あなたのお金を返すときに私に勝つの宝くじですか?
На самом деле я просто найти Я еще 1000 долларов в моем кошельке.

Google AJAX Language in ASP.NET

I found another treasure in Google CodeGoogle AJAX Languages API. Now I can detect and translate blocks of text in a web page using Javascript. I am thinking to build a custom server control which inherits System.Web.UI.WebControls.Label, and this control is able to translate its Text and Tooltip to the language specified when the control is rendered.

Today I just try the “Hello World” example:

<%@ Page Language=”C#” AutoEventWireup=”true” CodeBehind=”Default.aspx.cs” Inherits=”Posts._Default” %>

<!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>Google AJAX Language</title>

<script type=”text/javascript” src=”http://www.google.com/jsapi”></script>

<script type=”text/javascript”>

google.load(“language”, “1″);

function pageLoad(sender, args)

{

translate_text(“<%=lblMessage1.ClientID%>”, “zh-TW”);

translate_text(“<%=lblMessage2.ClientID%>”, “zh-TW”);

}

function translate_text(element_id, language_code)

{

var text = $get(element_id).innerHTML;

google.language.detect(text, function(result) {

if (!result.error && result.language) {

google.language.translate(text, result.language, language_code, function(result) {

if (result.translation) {

$get(element_id).innerHTML = result.translation;

}

});

}

});

}

</script>

</head>

<body>

<form id=”form1″ runat=”server”>

<asp:ScriptManager ID=”ScriptManager1″ runat=”server” />

<h3>Google AJAX Language</h3>

<div>

<asp:Label ID=”lblMessage1″ runat=”server” Text=”Hello World” />

<br />

<asp:Label ID=”lblMessage2″ runat=”server” Text=”This is a test page demonstrating the language translation.” />

</div>

</form>

</body>

</html>

The output is:

Google AJAX Language
世界您好
這是一個測試網頁上展示的語言翻譯。

jQuery and ASP.NET AJAX PageMethods

In ASP.NET AJAX there are two ways to make AJAX calls to retrieve data from server side – Web Services and Page Methods. For the project I am working on, based on the design and flexibility for page developers, I use PageMethods to query data from the server side and it worked well. But there is one minor issue with PageMethods – you can’t assign Javascript inner functions for the success and error callbacks. It’s not a big deal, but I like the way that Google Map uses closures. So I searched for alternatives and I found jQuery has the ajax() function which is what I need.

Here I build two pages – one uses PageMethods, another used jQuery – for comparison of the syntax and usage.

I use two basic container classes for my data passed between client and server:

using System;

namespace Posts

{

[Serializable]

public class LatLong

{

public double? latitude;

public double? longitude;

}

}

using System;

namespace Posts

{

[Serializable]

public class MapView

{

public LatLong center;

public int? zoom;

}

}

The MapViewData class provides a static method to generate random map view data:

using System;

using System.Collections;

using System.Collections.Generic;

namespace Posts

{

public class MapViewData

{

public const double DefaultLatitude = 49.266214d;

public const double DefaultLongitude = -122.998577d;

public const int DefaultZoom = 12;

public static List<MapView> GenerateMapViewData(int count)

{

List<MapView> data = new List<MapView>();

Random rand = new Random();

for (int i = 0; i < count; i++)

{

double delta = rand.NextDouble() * 0.001d;

LatLong latlong = new LatLong();

latlong.latitude = DefaultLatitude + delta;

latlong.longitude = DefaultLongitude + delta;

MapView view = new MapView();

view.center = latlong;

view.zoom = DefaultZoom;

data.Add(view);

}

return (data);

}

}

}

The following static page method is in both ASP.NET AJAX and jQuery pages for return a collection of MapView objects to Javascript:

[WebMethod]

public static List<MapView> GetMapViewData(int count)

{

return (MapViewData.GenerateMapViewData(count));

}

The first example is the ASP.NET AJAX with PageMethods. Couple things to notice:

  1. EnablePageMethods=”true” in the ScriptManger – to enable the static page methods in ASP.NET page can be called from Javascript.
  2. In success_callback, the result is the collection of MapView objects. We can loop through the collection and access properties of each object.

<%@ Page Language=”C#” AutoEventWireup=”true” CodeBehind=”Compare1.aspx.cs” Inherits=”Posts.Compare1″ %>

<!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>ASP.NET AJAX</title>

<script type=”text/javascript”>

function get_mapview_data(count)

{

PageMethods.GetMapViewData(

count,

success_callback,

error_callback

);

}

function success_callback(result, context, method)

{

var sb = new Sys.StringBuilder();

for (var i in result) {

sb.append(“center -> (“ + result[i].center.latitude + “, “ + result[i].center.longitude + “), “);

sb.append(“zoom -> “ + result[i].zoom);

sb.append(“<br/>”);

}

$get(“mapview_data”).innerHTML = sb.toString();

}

function error_callback(error, context, method)

{

$get(“mapview_data”).innerHTML = “Error –> “ + error.get_message();

}

</script>

</head>

<body>

<form id=”form1″ runat=”server”>

<asp:ScriptManager ID=”ScriptManager1″ runat=”server” EnablePageMethods=”true” />

<div>

<a href=”javascript:get_mapview_data(20);”>Get MapView data</a>

</div>

<br />

<div id=”mapview_data”></div>

</form>

</body>

</html>

The second example isjQuery with PageMethods. Couple things to notice too:

  1. The URL to request is the current page URL concatenated with “/PageMethodName”.
  2. I use JSON2 Javascript library to parse (deserialize) the XmlHttpRequest object returned in the error callback. You can use Sys.Serialization.JavaScriptSerializer.deserialize() to do the same thing here.
  3. There is no need to use ScriptManager.

<%@ Page Language=”C#” AutoEventWireup=”true” CodeBehind=”Compare2.aspx.cs” Inherits=”Posts.Compare2″ %>

<!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>jQuery</title>

<script type=”text/javascript” src=”/JavaScript/jquery-1.2.6.min.js”></script>

<script type=”text/javascript” src=”/JavaScript/json2.js”></script>

<script type=”text/javascript”>

function get_mapview_data(count)

{

$.ajax({

type: “POST”,

url: window.location.href + “/GetMapViewData”,

data: “{\”count\”:\”" + count + “\”}”,

contentType: “application/json; charset=utf-8″,

dataType: “json”,

success: function(xhr, status) {

var sb = “”;

for (var i in xhr) {

sb += “center -> (“ + xhr[i].center.latitude + “, “ + xhr[i].center.longitude + “), “;

sb += “zoom -> “ + xhr[i].zoom;

sb += “<br/>”;

}

$(“#mapview_data”).html(sb);

},

error: function(xhr, status, error) {

var err = JSON.parse(xhr.responseText);

$(“#mapview_data”).html(“Error –> “ + err.Message);

}

});

}

</script>

</head>

<body>

<form id=”form1″ runat=”server”>

<div>

<a href=”javascript:get_mapview_data(20);”>Get MapView data</a>

</div>

<br />

<div id=”mapview_data”></div>

</form>

</body>

</html>

I use Firebug to check the Post and Response in both XmlHttpRequests, they are the same in both pages.

JavaScriptSerializer example

Today at work I had a chance to try the JavaScriptSerializer class in the System.Web.Script.Serialization namespace. My task was simple enough – deserialize a JSON string to an object then serialize the object back to a JSON string. Before I used the two methods Deserialize() and Serialize(), I had to create two classes for my experiment:

[Serializable]

public class LatLong

{

public double? latitude;

public double? longitude;

}

[Serializable]

public class MapView

{

public LatLong center;

public int? zoom;

}

So here is the code to deserialize and Serialize:

using System.Text;

using System.Web.Script.Serialization;

protected void Page_Load(object sender, EventArgs e)

{

string example = “{\”center\”:{\”latitude\”:\”49.266214\”,\”longitude\”:\”-122.998577\”},\”zoom\”:\”12\”}”;

JavaScriptSerializer serializer = new JavaScriptSerializer();

// Deserialize

MapView view = serializer.Deserialize<MapView>(example);

StringBuilder sb = new StringBuilder();

sb.Append(“center = (“ + view.center.latitude.ToString() + “, “ + view.center.longitude.ToString() + “)” + “<br/>”);

sb.Append(“zoom = “ + view.zoom.ToString());

litDeserialization.Text = sb.ToString();

// Serialize

string jsonString = serializer.Serialize(view);

litSerialization.Text = jsonString;

}

The result is like:

Deserialization:

center = (49.266214, -122.998577)
zoom = 12

Serialization:

{“center”:{“latitude”:49.266214,”longitude”:-122.998577},”zoom”:12}