Skip navigation

Take Control - 30 Oct 2009

The ASP.NET AJAX 3.5 InlineEditLabel Control

asp:Feature

LANGUAGES: C#

ASP.NET VERSIONS: 3.5

 

Take Control

The ASP.NET AJAX 3.5 InlineEditLabel Control

 

By Bilal Haidar

 

The AJAX revolution brought many ideas to developers ideas with which to experiment and create a better Web experience for their users. But many techniques were difficult, if not impossible. However, with ASP.NET AJAX 3.5, a lot can be done on the client side with a minimum of work on the server side.

 

Microsoft offered ASP.NET AJAX 1.0 Extensions as a separate download to enable AJAX in ASP.NET applications. With Visual Studio 2008 and ASP.NET 3.5, ASP.NET AJAX is now part of ASP.NET 3.5. In other words, when you create a new ASP.NET application in Visual Studio 2008 targeting the .NET 3.5 Framework, it will be enabled automatically with AJAX.

 

One of the most attractive features on a Web site is inline edit. Inline edit allows administrators, or any group of trusted people on a Web site, to click on static text; the static text is then shown in an editable textbox, allowing the user to change the text. Changes on the displayed text will be shown automatically after it has been saved on the server.

 

This is not a complicated control to develop; the trick lies in how to develop a flexible inline edit control that allows developers to configure it in an easy way and provide a simple interface to handle the change of text event. This is exactly what this article will demonstrate by developing a server-side control for inline edit that can be easily dragged and dropped from the Toolbox inside Visual Studio and subscribing to its required events on the client side in a straightforward and simple fashion.

 

Introduction

There were several free and commercial AJAX libraries available online before the days of ASP.NET AJAX. Most of them allow you to AJAX-enable your Web sites without requiring any knowledge of JavaScript (the basis for any AJAX library).

 

What is beautiful about ASP.NET AJAX is the availability of both server-side controls and a very rich client library that gives developers the flexibility to build client-side controls.

 

Discussing the ASP.NET AJAX Library is not the topic of this article, but it is important to mention that the new client library has extended the original JavaScript with many features, including Namespace, Class, Enum, Inheritance, and Interface all of which are based on the prototype pattern in JavaScript.

 

In addition, a set of classes has been added that helps in developing client-side controls. Controls can be either extender or behavioral controls that extend the functionality of an existing ASP.NET server control and custom controls that you develop from scratch, and thus are standalone controls.

 

We ll create in this article a new standalone AJAX control that provides inline edit functionality on a Web site. While developing the control, explanations will be given on every section necessary to create a custom client-side control.

 

References to ASP.NET AJAX 3.5

This article assumes you have a fair knowledge of using ASP.NET AJAX 3.5 and is not intended to explain how to use the library. If you feel you need more information on ASP.NET AJAX 3.5, I recommend you check the official ASP.NET AJAX Web site at http://ajax.asp.net. In addition, Rob Bagby, a Microsoft Evangelist, presents a series of Web casts on AJAX that cover the server side and client side of ASP.NET AJAX 1.0 Extensions, which are still valid for ASP.NET AJAX 3.5 (details are available at Rob s blog at http://blogs.msdn.com/bags/default.aspx). Moreover, take a look at the series of articles published on http://www.aspalliance.com, including Inline-Edit Control Using ASP.NET 2.0 AJAX 1.0 Extensions , Create Client Controls in ASP.NET 2.0 AJAX 1.0 Extensions , Extending the ImageMap HTML Control with AJAX 1.0 Extensions , How to Show Messenger-Like Popups Using AJAX , Tips and Tricks: ASP.NET AJAX 1.0 and User Controls , and Tips and Tricks: ASP.NET 2.0 AJAX 1.0 Extensions and Master Pages .

 

The InlineEditLabel Control

To start, let s create a new Visual Studio 2008 empty solution. Then add two projects, an ASP.NET AJAX server control that contains the code for the control being developed and a Web site to test the control (see Figure 1). Developing an ASP.NET AJAX control is composed of two parts: The server side and the client side.

 


Figure 1: The InlineEditLabel control solution.

 

The InlineEditLabel Control: Server Side

When a new ASP.NET AJAX server control project has been added, two files are shown: a C# or VB.NET class and a JavaScript file. In this section, the C# class details will be explained.

 

The C# class extends the ScriptControl class that internally extends the WebControl class and implements the IScriptControl interface. The WebControl class is used to develop ASP.NET server controls. It is similar to the Control base class, but with more support for styling features.

 

The IScriptControl contains two main methods that should be implemented on the server side: GetScriptDescriptors and GetScriptReferences. The GetScriptDescriptors method returns a collection of ScriptControlDescriptors. Each ScriptControlDescriptor represents a single control and will be used to define and initialize the control on the client side. The constructor of the ScriptControlDescriptor takes as input the name of the client-side component and the Client ID of the UI element to which this control will be bound on the client side.

 

Once a new instance of the ScriptControlDescriptor is created, a developer should add using the AddProperty method of the ScriptControlDescriptor all the properties and events required by the client control.

 

As a result, this method will be queried to return a collection of ScriptControlDescriptors; for each descriptor, the initialization code will be placed in the ASPX page. Initialization code includes the UI element to bind to and values for all the properties and events required by the control. More on the initialization code will be demonstrated later in this article. Figure 2 shows the code for the GetScriptDescriptors method.

 

IEnumerable function advagg_mod_1() { // Count how many times this function is called. advagg_mod_1.count = ++advagg_mod_1.count || 1; try { if (advagg_mod_1.count <= 40) { IScriptControl.GetScriptDescriptors()

 {

   ScriptControlDescriptor desc = new ScriptControlDescriptor

     ("Bhaidar.AJAX.Controls.InlineEditLabel", this.ClientID);

   if (!string.IsNullOrEmpty(this.Text))

   {

     desc.AddProperty("originalText", this.Text);

   }

   if (!string.IsNullOrEmpty(this.ToolTip))

   {

     desc.AddProperty("toolTip", this.ToolTip);

   }

   if (!string.IsNullOrEmpty(this.CssClass))

   {

     desc.AddProperty("cssClass", this.CssClass);

   }

   if (!string.IsNullOrEmpty(this.CssHoverClass))

   {

     desc.AddProperty("cssHoverClass", this.CssHoverClass);

   }

   if (!string.IsNullOrEmpty(this.UpdateButtonText))

   {

     desc.AddProperty("updateButtonText", this.UpdateButtonText);

   }

   if (!string.IsNullOrEmpty(this.CancelButtonText))

   {

     desc.AddProperty("cancelButtonText", this.CancelButtonText);

   }

   if (!string.IsNullOrEmpty(this.UpdateEvent))

   {

     desc.AddEvent("update", this.UpdateEvent);

   }

   yield return desc;

 }

Figure 2: The GetScriptDescriptors method.

 

As the code shows, several properties have been added to the ScriptControlDescriptor, including:

  • originalText: This property holds the text to be shown when the control is in display mode and not edit mode.
  • cssClass: This property specifies the CSS class to apply on the UI element that holds the contents of the control. The UI element is usually a div element.
  • cssHoverClass: This property specifies the CSS class to be applied on the UI element when the mouse hovers over the UI element.
  • updateButtonText: This property holds the text to be displayed for the Update button. When the control is in edit mode, a text area and two buttons (Update and Cancel) will be displayed. This property allows you to change the default text.
  • cancelButtonText: This property holds the text to be displayed for the Cancel button, and behaves as does updateButtonText.
  • update: This property holds the name of the client-side function that will get executed when the Update button is clicked. Usually a developer would hook to this event and add any custom code to reflect the changes of the displayed text on the server.

 

The GetScriptReference method returns a collection of ScriptReference objects. Each ScriptReference object represents a JavaScript file to be embedded into the ScriptManager on the page. For each control there should be an accompanying JavaScript file that holds the functionality of the control on the client side. At this stage it is very important to right-click on the included JavaScript file and then press on properties (from Build Action select Embedded Resource). This makes sure the client-side code will be embedded within the control assembly. Once the client-side file is embedded as a resource, it can be retrieved while adding the ScriptReference objects. Figure 3 shows the details of the GetScriptReference method.

 

IEnumerable IScriptControl.GetScriptReferences()

{

 ScriptReference inlineEditLabel = new ScriptReference(

   this.Page.ClientScript.GetWebResourceUrl(

     this.GetType(),

     "Bhaidar.AJAX.Controls.InlineEditLabel.js"));

 return new ScriptReference[] {inlineEditLabel};

}

Figure 3: The GetScriptReference method.

 

In addition to subscribing to the GetScriptDescriptors and GetScriptReference methods, two additional methods are to be implemented. The first is OnPreRender. In this method, code should be added so the current control gets registered by the ScriptManager instance on the page.

 

The second method is Render. The implementation code for this method is simple: only a div tag is to be rendered on the page for this control. This div element represents the UI element to which the control is bound, so the contents of the control will be placed within this UI div element. Figure 4 shows the details of the OnPreRender and Render methods.

 

protected override void OnPreRender(EventArgs e)

{

 base.OnPreRender(e);

 ScriptManager scriptManager = ScriptManager.GetCurrent(Page);

 if (scriptManager == null)

 {

   throw new InvalidOperationException(

     "ScriptManager required on the page.");

 }

 scriptManager.RegisterScriptControl(this);

}

protected override void Render(HtmlTextWriter writer)

{

 base.Render(writer);

 writer.AddAttribute(HtmlTextWriterAttribute.Id, this.ClientID);

 writer.RenderBeginTag(HtmlTextWriterTag.Div);

 writer.RenderEndTag();

 if (!DesignMode)

 {

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

 }

}

Figure 4: Implementation of the OnPreRender and Render methods.

 

One note to mention here is how easy it is to access the current instance of the ScriptManager located on the ASPX page. The ScriptManager class has a static method named GetCurrent that takes as input the Page instance and returns the ScriptManager instance so the required JavaScript files can be added correctly to the ScriptManager s Script section.

 

The InlineEditLabel Control: Client Side

The client-side part of developing an ASP.NET AJAX server control requires creating a JavaScript client-side component using the Prototype design pattern. The client-side component is a lengthy one, which is why each section will be explained separately. Figure 5 shows the code for the control s constructor.

 

// Register the namespace that holds the InlineEditLabel class

Type.registerNamespace("Bhaidar.AJAX.Controls");

// Define the constructor of the InlineEditLabel class

// which takes as input

Bhaidar.AJAX.Controls.InlineEditLabel = function(element) {

 Bhaidar.AJAX.Controls.InlineEditLabel.initializeBase(this, [element]);

 this._originalText= 'Default Text';

 this._toolTip= '';

 this._cssClass= '';

 this._cssHoverClass='';

 this._editMode = false;

 this._updateButtonText = 'Update';

 this._cancelButtonText = 'Cancel';

 // Define Handlers

 this._clickHandler = null;

 this._updateHandler = null;

 this._cancelHandler = null;

 this._mouseOverHandler = null;

 this._mouseOutHandler = null;

}

Figure 5: The InlineEditLabel client control s constructor.

 

The script starts by registering a new client-side namespace. The namespace defined here is Bhaidar.AJAX.Controls. The control s constructor is a normal JavaScript function that takes as input the UI element bound to the control.

The first step inside a constructor is to initialize the base class by calling the base class constructor. It is very important that the base class gets initialized. In ASP.NET AJAX, controls inherit from the Sys.UI.Control class. For more details, I recommended you read ASP.NET AJAX Control Development by Kazi Manzur Rashid, available at http://dotnetslackers.com/articles/ajax/ASPNETAJAXControlDevelopment.aspx.

 

After initializing the base class, initializing the private fields is a must at this step. Why? The trick in ASP.NET AJAX controls is that if the property value was not defined in the initializing code of the control on the ASPX page, the setter of the control will not be called, so if there is a default value to be assigned to any property regardless of whether the property was assigned a value at initialization time or not, it should be assigned at this stage of the control.

 

Notice that the number of private fields initialized in the constructor is the same number of the properties added to the ScriptControlDescriptor and returned by the GetScriptDescriptors method.

 

In addition to initializing the private fields, the event handlers also should be initialized. In this control there are five required event handlers. A handler for the click event on the displayed text, a handler for the update event when the Update button is clicked, a handler for the cancel event when the Cancel button is clicked, and two handlers for the mouse over/out on the displayed text.

 

Now that the constructor is defined, it s time to add the properties and functions to the prototype of the control, which the control inherits automatically from its prototype. Figure 6 shows the code for the getter and setter for one of the properties of the control.

 

Bhaidar.AJAX.Controls.InlineEditLabel.prototype = {

 // Getter for the OriginalText property

 get_originalText: function() {

   return this._originalText;

 },

 // Setter for the OriginalText property

 set_originalText: function(value) {

   this._originalText = value;

 },

Figure 6: The InlineEditLabel sample property definition.

 

There is no notion of properties in the ASP.NET AJAX Client Library. Properties are defined as getter and setter functions. The getter function returns the private field and the setter fills the private field.

 

Another important method to include in an ASP.NET AJAX control is the initialize function (see Figure 7).

 

initialize: function() {

// First thing to do, call the base

// initialize method, makes sure that now you can use

// any defined event, function, or property on the base class

 Bhaidar.AJAX.Controls.InlineEditLabel.callBaseMethod(this, 'initialize');

 // Define the handlers

 if (this._clickHandler == null)

   this._clickHandler = Function.createDelegate(this, this._onClick);

 if (this._updateHandler == null)

   this._updateHandler = Function.createDelegate(this, this._onUpdate);

 if (this._cancelHandler == null)

   this._cancelHandler = Function.createDelegate(this, this._onCancel);

 if (this._mouseOverHandler == null)

   this._mouseOverHandler = Function.createDelegate(this, this._onMouseOver);

 if (this._mouseOutHandler == null)

   this._mouseOutHandler = Function.createDelegate(this, this._onMouseOut);

 // Initialize the control with original text

 this._renderOriginalContents();

 // Set events on the control

 this._setControlEvents();

},

Figure 7: The InlineEditLabel initialize function.

 

The first statement in Figure 7 is a call to the initialize function of the base class. Once again, this is a must so the base class initialize function gets executed. The next step shown is to initialize all the event handlers. Event handlers are defined by using the Function.createDelegate function. This function takes as input two parameters: the class instance and the function to execute. This makes sure the event handler being defined refers to a function that is to be executed in the context of the class instance sent as the first parameter of the createDelegate function. Hence, all the properties and initialization code defined for a control s instance can be accessed from the event handler defined.

 

After defining the event handlers, the renderOriginalContents function is called (see Figure 8). This is a utility function that constructs the original display of the control.

 

_renderOriginalContents: function() {

 // Get a reference to the passed in element

var element = this.get_element();

 // Clear content from the element

element.innerHTML = '';

// Set the text

if (this.get_originalText() == '')

 this.set_originalText("Default Text");

element.innerHTML = this.get_originalText();

// Set the cursor to a hand cursor with a tooltip

element.style.cssText = "cursor:pointer";

// Set the Tooltip for the control

 element.setAttribute("title", this.get_toolTip());

 // Set the CssClass for this control

 Sys.UI.DomElement.addCssClass(element, this.get_cssClass());

},

Figure 8: The renderOriginalContents function.

 

The renderOriginalContents function sets the text in the UI element to the text defined in the originalText property. In addition, the Tooltip and CSS class properties are assigned to the UI div element.

 

Going back to the initialize function, the final statement in the function was a call to another utility function named setControlEvents (see Figure 9).

 

_setControlEvents: function() {

 var target = this.get_element();

 $addHandlers(target, {'click':this._clickHandler,

    'mouseover':this._mouseOverHandler,

   'mouseout':this._mouseOutHandler}, this);

}

Figure 9: The setControlEvents function.

 

The setControlEvents function adds to the main UI element event handlers for the click, mouse over, and mouse out events. Each event is assigned an event handler or a delegate. Each delegate has been defined to a local function to be executed once the event occurs.

 

There still remains a very important function to be implemented: the dispose function. The base class dispose function should be called, all the event handlers that have initialized before should be deleted, and all the event handlers attached to the UI element should be cleared out.

 

When defining an event for a control that is to be processed on the ASPX page, several things should be added to the control s client code: an event handler or delegate, two functions to add or remove handlers for the event (these are called by the ScriptManager to add or remove any binding to client-side functions), and a function referenced by the delegate (this method can perform any custom code, then call the event handler on the ASPX page). Figure 10 shows the code for the local event handler function and the two functions used to add/remove event handling binding.

 

add_update : function(handler) {

 this.get_events().addHandler('update', handler);

},

remove_update : function(handler) {

 this.get_events().removeHandler('update', handler);

},

_onUpdate : function(e) {

 var handler = this.get_events().getHandler('update');

 // Check if there is any subscriber of this event

   if (handler != null)

 {

   // Get the value inside the textarea

   var data = $get(this._editorID()).value;

   var inlineUpdateLabelEventArgs =

      new Bhaidar.AJAX.Controls.InlineUpdateLabelEventArgs(data);

   // Set the original text to the new one

   this.set_originalText(data);

   // Set the processing text

   this._showUpdateProgress();

   // Set this control to be in the read-only mode

   this._editMode = false;

   handler(this, inlineUpdateLabelEventArgs);

 }

},

Figure 10: Update the event handlers.

 

The add/remove event handler functions should be named with the same name of the event exposed on the control with the add_ or remove_ prefixes concatenated. These functions will be called by the ScriptManager, which is why they should have the same name of the event exposed.

 

The add function adds the update event handler to the list of events on the UI element and the remove function does the inverse.

 

The onUpdate function will be called when the update function is clicked. This function defines a new instance of the InlineEditLabelEventArgs object. This object is a custom EventArgs object that will hold the data displayed in the control, set the control in the read-only mode, and, finally, call the attached event handler passing as a parameter to the attached event handler the InlineEditLabelEventArgs instance. The attached event handler is a client-side function defined by the developer on the ASPX page. The design of this control gives the developer more freedom in selecting the preferred way to update the data on the server. For instance, the developer can now process the changed text by either calling a Web service method, page method, or performing a WebRequest method call.

 

Another important event handler to mention is the onClick event handler (see Figure 11). This handler will be called when a click event is performed on the InlineEditLabel control.

 

_onClick : function(e) {

 if (this._editMode == false)

 {

   // Set this control to be in edit mode

   this._editMode = true;

   // Remove the hover class

   this._mouseOutHandler(e);

   this._renderEditorControl();

 }

},

Figure 11: The onClick event handler.

 

The function in Figure 11 sets the control in edit mode and calls another utility function named renderEditorControl (see Figure 12). This function will display the InlineEditLabel control in edit mode, and place the displayed text in a text area with two buttons: one to update and one to cancel the changes.

 

_renderEditorControl: function()

{

 // This method constructs the control

 // when the user is editing the text.

 // The structure is as follows:

 //

 //

 //

 //  value="Update" />  

 //

 //  value="Cancel" />

 // Get a reference to the passed in element

var element = this.get_element();

 // Get the bounds of the displayed text

 // and set the width/height of the editor to those values

 var width = Sys.UI.DomElement.getBounds(element).width - 5;

 var height =  (2) * Sys.UI.DomElement.getBounds(element).height;

 // Clear content from the element

element.innerHTML = '';

 // Add a Textbox with two buttons: update and cancel

 var textArea = document.createElement('textarea');

 textArea.setAttribute("id", this._editorID());

 textArea.setAttribute("name", this._editorID());

 textArea.style.cssText = "width:" + width + "px;height:" +

   height + "px;";

 textArea.innerHTML = this.get_originalText();

element.appendChild(textArea);

$get(this._editorID()).focus();

$get(this._editorID()).select();

// Add the Update button

var btnUpdate = document.createElement('input');

btnUpdate.setAttribute("id", this._updateButtonID());

btnUpdate.setAttribute("type", "submit");

btnUpdate.setAttribute("name", this._updateButtonID());

btnUpdate.setAttribute("value", this.get_updateButtonText());

element.appendChild(btnUpdate);

 $addHandler(btnUpdate, 'click', this._updateHandler);

// Add the space span

var space= document.createElement('span');

space.innerHTML = "  ";

element.appendChild(space);

// Add the Cancel button

var btnCancel = document.createElement('input');

btnCancel.setAttribute("id", this._cancelButtonID());

btnCancel.setAttribute("type", "submit");

btnCancel.setAttribute("name", this._cancelButtonID());

btnCancel.setAttribute("value", this.get_cancelButtonText());

element.appendChild(btnCancel);

$addHandler(btnCancel, 'click', this._cancelHandler);

},

Figure 12: The renderEditorControl function.

 

The function clears all the UI inside the div element, then adds the text area and two buttons. An event handler is added to the Update button s click event so the client (ASPX page) gets a chance to process the event handler. In addition, an even handler is added to the Cancel button so the InlineEditLabel control gets back to its original state (the read-only state). Finally, the InlineEditLabel class should be registered by adding this line of code:

 

Bhaidar.AJAX.Controls.InlineEditLabel.registerClass(

  Bhaidar.AJAX.Controls.InlineEditLabel , Sys.UI.Control);

 

The code above uses the inherited registerClass function on the InlineEditLabel class that takes as input the client class name and the base class to inherit from; in this case, it s the Sys.UI.Control class.

 

Testing the InlineEditLabel Control

In this section, the ASP.NET Web site that has been created will be used to test the InlineEditLabel control. Once the ASP.NET AJAX server control is successfully built, an assembly is created. Adding a reference to that assembly adds the control to the Visual Studio toolbox. To test the control, simply drag the control to an ASPX page. One requirement is that an instance of the ScriptManager class must be placed on the ASPX page. Figure 13 shows the server control when placed inside an ASPX page.

 

<%@ Register Assembly="InlineEditLabel"

 Namespace="Bhaidar.AJAX.Controls" TagPrefix="cc1" %>

 

   ID="InlineEditLabel1"

   runat="server"

   Text="This is a default text to be changed

     during the demo!!"

   ToolTip="Click here to edit"

   CssClass=""

   CssHoverClass="hover"

   UpdateButtonText="Update"

   CancelButtonText="Cancel"

   UpdateEvent="OnUpdate"

 />

Figure 13: The InlineEditLabel server control placed on an ASPX page.

 

As shown, the control s assembly is registered on top of the ASPX page and the control itself is no different from any other ASP.NET server control. The control s properties can be assigned at design time or programmatically from the code-behind.

 

The UpdateEvent event handler shall be assigned to the name of a client-side function that should be added to the ASPX page. This function is a JavaScript function that gets executed when the Update button, shown when the control is in edit mode, is clicked.

 

Running the ASP.NET Web site shows how the above control is shown in the read-only mode. Figure 14 shows the ASPX page displaying the InlineEditLabel control in read-only mode.

 


Figure 14: ASPX page showing the InlineEditLabel control in read-only mode.

 

The page is shown when the mouse is hovering over the static text displayed by the control. When the mouse is clicked, the control shall display the text in edit mode. Figure 15 shows the control in edit mode.

 


Figure 15: The InlineEditLabel control shown in edit mode.

 

The text is displayed and highlighted. When the changes are done on the text displayed, the Update button can be clicked and the client-side event handler will then fire to reflect the changes on the server. The Cancel button might also be clicked to set the control back to its original read-only mode. The update event handler function can be defined in its simplest form, as shown in Figure 16.

 

Figure 16: The update client-side event handler function.

 

The OnUpdate event handler takes as input two arguments: the sender and the args parameters. The args parameter represents the instance of the InlineUpdateLabelEventArgs class. The data assigned to the args instance can be retrieved by using the get_data function. This function gets the updated text in the text area. The text retrieved by this function is controlled by the text assigned to the EventArgs class in the onUpdate event handler inside the client-side code of the control defined in Figure 16.

 

At this stage a server call can be performed and, once the response is sent back from the server, the renderOriginalContents function should be called so the control hides the internal UpdateProgress. In this case it is simply showing the saving ... statement, and shows the text placed inside the originalText property. If the text to be shown doesn t require any manual changes, there is no need to call the set_originalText function and if the text to be shown requires a manual change, then setting the text to be displayed can be done by calling the set_originalText function, then a call to the renderOriginalContents function. Notice that the $find function has been used to find the instance of the InlineEditLabel control on the ASPX page.

 

Conclusion

This article explained how to create an inline edit ASP.NET AJAX server control. It was shown that developing such a control requires writing both server-side and client-side code. The InlineEditLabel control can show text in read-only mode, and when the text is clicked by the mouse, the control changes its display to show the text in an editable form (in this case a text area with two buttons).

 

Source code accompanying this article is available for download.

 

Bilal Haidar is a Microsoft MVP in ASP/ASP.NET (since 2004). He is an MCP, MCTS, MCPD, and MCT holder, and the author of Professional ASP.NET 3.5 Security, Membership, and Role Management with C# and VB (Wrox, 2008). He is a Telerik MVP and the Webmaster for the LebDev user group. Bilal is one of the top posters on the ASP.NET official forums, as well as a moderator. He is a senior software developer at CCC, a multinational construction company based in Athens, Greece. You can check his published articles on http://www.aspalliance.com and http://www.code-magazine.com. He runs his own blog at http://www.bhaidar.net. Contact him with any questions or for help at mailto:[email protected].

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish