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
- Text property is overridden – this is the text will be translated to target language based on the LanguageCode property.
- LanguageCode property – See Google Language Enum for valid codes.
- 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
- 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.
- 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
- Register web server control – <%@ Register Namespace=”Posts.AjaxControl” Assembly=”Posts” TagPrefix=”ac” %>
- Link to the external Javascript http://www.google.com/jsapi.
- Include Google AJAX Lauguage API in the page – call google.load(“language”, “1″); to load version 1 of the AJAX Language API.
- 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 Code – Google 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:
- EnablePageMethods=”true” in the ScriptManger – to enable the static page methods in ASP.NET page can be called from Javascript.
- 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:
- The URL to request is the current page URL concatenated with “/PageMethodName”.
- 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.
- 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}
Comments (1)
Leave a Comment
Comments (2)