Click here to Skip to main content
15,357,682 members
Articles / Web Development / ASP.NET
Posted 4 Jun 2008

Tagged as


51 bookmarked

Building an ImageTag Extender like Facebook Image Tagger - Part 1

Rate me:
Please Sign up or sign in to vote.
4.75/5 (15 votes)
4 Jun 2008CPOL5 min read
An article about building an ImageTag Extender like Facebook Image Tagger


A few days ago, one of my clients asked for an Image tagging control which looks like the Facebook Photo Tagging application. That one was built on PHP but my application was based on ASP.NET, so I needed to find some solution with ASP.NET and Ajax. The result is this ImageTag Extender that allows to tag an image with respective name tags and that can be stored through Ajax enabled Web service. Before we jump in-depth to understand the extender, we will discuss the basics of Ajax Control Toolkit Extenders.

ASP.NET Ajax Extender

Atlas brought the concept of extenders to provide client side behaviors to controls. Extenders attach JavaScript, DHTML and stylesheets to the control they takes as the target control.

The ImageTag Extender

My ImageTag Extender takes an ASP Image control as the target Control. It also takes few properties such as another image to act as a Save button, a text box which takes the tag name, and a Container class name for a ASP Panel control which will contain the TextBox and the Button and a <div> which will target the tag area and be visible when the user clicks on a point to tag.

The ImageTagExtender declaration will be as follows:

<cc1:ImageTagExtender ID="ImageTagExtender1" runat="server"
    TargetControlID="Image1" TargetTextBox="TextBox1" TextContainerClass="Float">

The code for the Container Panel is given below:

<asp:Panel ID="Panel1" runat="server" CssClass="Float">
    <Div style="border-style:dashed;border-width:medium;

    <asp:Image ID="Image2" runat="server" Height="32px" ImageUrl="~/BlueHills.jpg"
        Width="46px" />

    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>

The idea of the tag extender is to grab the point where the user clicks on the picture and save that point along with the tag name through a Web service method.

And while loading the image in view mode, load the tagged points through another Web method and place transparent <div> objects there and also please tag names which will be visible when the user moves the mouse over the <div> area.

I created the extender using Visual Studio 2008 ASP.NET Ajax Extender Project template because it prepares your web.config with the required declarations. You can also create the extender in Visual Studio 2005.

My Extender class expects three properties as I mentioned before and it takes an image object as its target, so you have to declare the properties in the ImageTagExtender class and also define the type of designer and the behavior file for the extender.

 public class ImageTagExtender : ExtenderControlBase {

The declaration for my properties is as follows:

    public string TargetButtonID
        return GetPropertyValue("TargetButtonID", "");
        SetPropertyValue("TargetButtonID", value);

    public string TargetTextBox
          return GetPropertyValue("TargetTextBox", "");
          SetPropertyValue("TargetTextBox", value);

    public string TextContainerClass
            return GetPropertyValue("TextContainerClass", "");
            SetPropertyValue("TextContainerClass", value);

The Behavior Class

In the ASP.NET Ajax library, you can define the namespace for your script class with the registerNamespace method.


The behavior file contains all the client side methods and properties.

The first method of the behavior file is the initializer which intializes all JavaScript objects.

 ImageTagExtender.ImageTagExtenderBehavior = function(element) {
    ImageTagExtender.ImageTagExtenderBehavior.initializeBase(this, [element]);
    // TODO : (Step 1) Add your property variables here
    this._targetButtonIDValue = null;
    this._targetTextBox = null;
    this._textContainerClass = null;

ImageTagExtender.ImageTagExtenderBehavior.prototype = {
    initialize : function() {
        ImageTagExtender.ImageTagExtenderBehavior.callBaseMethod(this, 'initialize');
        // TODO: add your initialization code here
        $addHandler(this.get_element(), 'click',
        Function.createDelegate(this, this._onclick));
        var sButton = $get(this._targetButtonIDValue) ;
        $addHandler(sButton,'click',Function.createDelegate(this, this._onbuttonclick));
//        this._onclick();

    dispose : function() {
        // TODO: add your cleanup code here
        ImageTagExtender.ImageTagExtenderBehavior.callBaseMethod(this, 'dispose');

Here $addHandler is a JavaScript shortcut to add handlers to objects dynamically. It takes the object, event name and a handler as parameters.

Next, you have to declare the properties in your script. Here one thing is mandatory, that is to strictly follow the property convention as follows:

(get + _ + PropertyName)

Here are my property declarations:

// TODO: (Step 2) Add your property accessors here
get_TargetButtonID : function() {
    return this._targetButtonIDValue;
set_TargetButtonID : function(value) {
    this._targetButtonIDValue = value;
get_TargetTextBox : function() {
    return this._targetTextBox;
set_TargetTextBox : function(value) {
    this._targetTextBox = value;

get_TextContainerClass: function() {
    return this._textContainerClass;

set_TextContainerClass : function(value) {
    this._textContainerClass = value;

The onclick function places the container panel on the point clicked and gets the tag name for that point.


Here is the code for the _onclick function:

_onclick : function() {

 var panel = this._findChildByClass(document,this._textContainerClass);
 var e = $get(this._targetButtonIDValue);
 var ev = window.event;
 var pos = Sys.UI.DomElement.getLocation(panel);
 var scrollX = document.body.scrollLeft ||

 var scrollY = document.body.scrollTop ||
    var clickX = window.event.x-pos.x+scrollX;
    var clickY = window.event.y- pos.y+scrollY;


Here the function _findChildByClass returns all the objects with a specified class name. I got this idea of grabbing objects from Omar Al Zabir's Drop Things project.

_findChildByClass : function(item, className)
       // First check all immediate child items
       var child = item.firstChild;
       while( child != null )
           if( child.className == className ) return child;
           child = child.nextSibling;

       // Not found, recursively check all child items
       child = item.firstChild;
       while( child != null )
           var found = this._findChildByClass( child, className );
           if( found != null ) return found;
           child = child.nextSibling;

Once I grabbed the floating panel, then I captured the mouse click event for the clicked points. I also deducted the scrollX and scrollY in order to be precise about the original point clicked on the image. Currently, this code only supports Internet Explorer 4+, I am working on cross browser aspects. Then I just placed the floating panel on the clicked point.

Saving the Tag

Once the floating panel is placed, then the user provides a tag name in the text box and clicks the Save button. Now my _onbuttonclick function gets fired to call a Web service to save the positions and the tag name.

_onbuttonclick : function()  {

    var panel = this._findChildByClass(document,this._textContainerClass);
    var pos = Sys.UI.DomElement.getLocation(panel);
    var text = $get(this._targetTextBox).value;

    var args = new Array(pos.x.toString() ,pos.y.toString(),text);


Here I grab the position for the floating panel and the text value of the text box and pass them to the Web service method TaggReciever.TaggReciever.SetTag.

The Ajax library takes a fully qualified name for the service method (i.e. Namespace.ClassName.MethodName). It takes three parameters:

  1. Method Parameters array
  2. Function Succeeded callback
  3. Function Failed Callback

If the Web method takes a single parameter, then it can be directly passed but if it consumes multiple parameters, then those have to be passed in the form of an array. Next, the Function Succeeded callback and Function Failed callback are the function names that are called at successful and failed asynchronous callbacks respectively.

Here, in success callback function, you will get the return value of the service method. In this example, our service returns back a string with a description of the tag.

Here is the code for my success callback function _setPosition:

 _setPosition : function(result){

I just put an alert with the result value:

Image 2

Here is the code for the Web method:

[WebService(Namespace = "")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    public class TaggReciever : System.Web.Services.WebService
    public string SetTag(string[] args)
      //...Save Values
    return string.Format("x:{0},y:{1},Text:{2}", args[0], args[1], args[2]);

One important thing over here is that you have to tag the service consumable through Ajax calls by adding a [System.Web.Script.Services.ScriptService] attribute to the Web service class. Here you can see that the set tag method is just returning the arguments. You can save the values or do whatever you want.

In the failed call back function, you can handle the error returned from the service.

_onerror : function(error){


In this article, I discussed about the extender and how to save the tags through service calls. In the next part of this article, I'll discuss about an extender for showing the tags and few more tips about parsing Web service returns from XML or JSON serializer.


  • 2nd June, 2008: Initial release


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


About the Author

Md. Asif Atick
Team Leader Prime Tech Ltd
Bangladesh Bangladesh
Asif is a Devoloper in who has been programming since early 2000 where he started with php and dug down to discover C# .Net. Since 2001 he has been working for AIUB (Amarican International University Bangladesh) on VUES project a student management system. He became an MCAD on 2002. He has been using C# and ASP.Net since 2001.

Comments and Discussions

QuestionPlease Pin
Member 82185787-Sep-11 1:44
MemberMember 82185787-Sep-11 1:44 
AnswerRe: Please Pin
Member 82185787-Sep-11 1:45
MemberMember 82185787-Sep-11 1:45 
GeneralMy vote of 5 Pin
anandpinku10-Nov-10 21:32
Memberanandpinku10-Nov-10 21:32 
QuestionCan this be done in ... Pin
Member 135831920-May-09 5:36
MemberMember 135831920-May-09 5:36 
Generalreply for image extender.dll Pin
kautuk_2317-Oct-08 4:53
Memberkautuk_2317-Oct-08 4:53 
GeneralNice work Pin
X204010-Jun-08 20:44
MemberX204010-Jun-08 20:44 
Generalnice, thx Pin
nadrealista9-Jun-08 20:25
Membernadrealista9-Jun-08 20:25 
GeneralRe: nice, thx Pin
Md. Asif Atick14-Jul-08 0:15
MemberMd. Asif Atick14-Jul-08 0:15 
GeneralRe: nice, thx Pin
Thomas O'Hanlon29-Aug-08 5:54
MemberThomas O'Hanlon29-Aug-08 5:54 
GeneralRe: nice, thx Pin
BeakZ18-Mar-10 5:16
MemberBeakZ18-Mar-10 5:16 
GeneralRe: nice, thx Pin
anandpinku10-Nov-10 21:35
Memberanandpinku10-Nov-10 21:35 
GeneralRe: nice, thx Pin
anandpinku10-Nov-10 21:34
Memberanandpinku10-Nov-10 21:34 

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.