Click here to Skip to main content
15,896,489 members
Articles / Web Development / HTML

Magic AJAX: Applying AJAX to your existing Web Pages

Rate me:
Please Sign up or sign in to vote.
4.82/5 (72 votes)
28 May 2007MIT12 min read 983.7K   2.7K   251  
How to apply AJAX technologies to your web pages without replacing ASP.NET controls and/or writing JavaScript code.
#region Copyright
/*
Copyright (C) 2005 Argiris Kirtzidis
This script is supplied "as is" without any form of warranty. I shall not be liable for
any loss or damage to person or property as a result of using this script. Use this script
at your own risk! You are licensed to use this script free of charge for commercial or
non-commercial use providing you do not remove the copyright notice or disclaimer.
*/
#endregion

using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.ComponentModel;

namespace Ajax.Controls
{
	#region Public Class RenderStartEventArgs
	/// <summary>
	/// It contains the HtmlTextWriter writer event argument to be used during a
	/// RenderStart event.
	/// </summary>
	public class RenderStartEventArgs : EventArgs
	{
		private bool abortRendering = false;
		private HtmlTextWriter writer;

		public bool AbortRendering
		{
			get { return abortRendering; }
			set { abortRendering = value; }
		}

		public HtmlTextWriter Writer
		{
			get { return writer; }
		}

		public RenderStartEventArgs(HtmlTextWriter writer)
		{
			this.writer = writer;
		}
	}
	#endregion

	/// <summary>
	/// The event handler for a RenderStart event.
	/// </summary>
	public delegate void RenderStartEventHandler(object sender, RenderStartEventArgs e);

	/// <summary>
	///	This control manages its appearance on the page using javascript that sends
	///	to the client during a CallBack.
	/// </summary>
	/// <remarks>
	/// It provides the basic functionality for controls like AjaxPanel. It manages
	/// its tag attributes using javascript.
	/// </remarks>
	public class RenderedByScriptControl : AjaxControl, IPreRenderByScriptEventHandler, IRenderedByScript
	{
		private bool isRenderedOnPage = false;
		private bool monitorVisibilityState = true;
		private string currentTagHtml = null;
		private string tagOnPageHtml = null;
		private bool isHtmlRendered = false;

		public event RenderStartEventHandler RenderStart;
		public event EventHandler RenderEnd;
		public event EventHandler PreRenderByScript;

		// Disable EnableViewState so that the properties it sustains doesn't interfere with
		// the funtionality provided by RenderedByScriptControl.
		[Browsable(false),DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public override bool EnableViewState
		{
			get	{ return false; }
			set	{ }
		}

		public override bool Visible
		{
			get
			{
				return base.Visible;
			}
			set
			{
				if ( CallBackHelper.IsCallBack
					&& IsRenderedOnPage
					&& MonitorVisibilityState
					&& value != base.Visible )
					CallBackHelper.WriteSetVisibilityOfElementScript (ClientID, value);

				base.Visible = value;
			}
		}

		/// <summary>
		/// Determines whether the RenderedByScriptControl control has been rendered
		/// on the page, either by normal rendering or by script.
		/// </summary>
		/// <remarks>
		/// IsRenderedOnPage is set to true during a normal rendering or a rendering
		/// by script. It is set to false only during a CallBackStartup.
		/// </remarks>
		[Browsable(false),DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
		public bool IsRenderedOnPage
		{
			get { return isRenderedOnPage; }
		}

		/// <summary>
		/// Gets or sets whether every time "Visible" property is changed
		/// CallBackHelper.WriteSetVisibilityOfElementScript method should be called.
		/// Default is true.
		/// </summary>
		/// <remarks>
		/// If this property is true, every time you change the "Visible" property during a
		/// CallBack, the display attribute of the style of the control on the page will
		/// be set to "" or "none" by calling the CallBackHelper.WriteSetVisibilityOfElementScript
		/// method.
		/// </remarks>
		[Bindable(false), Category("Behavior"), DefaultValue(true)] 
		public bool MonitorVisibilityState
		{
			get { return monitorVisibilityState; }
			set { monitorVisibilityState = value; }
		}

		/// <summary>
		/// Returns the tag name of the control.
		/// </summary>
		/// <returns></returns>
		public string GetTagName()
		{
			return TagKey.ToString();
		}

		public RenderedByScriptControl()
		{
			base.EnableViewState = false;
			this.ID = "";
		}
		public RenderedByScriptControl(HtmlTextWriterTag tag) : base(tag)
		{
			base.EnableViewState = false;
			this.ID = "";
		}

		/// <summary>
		/// It defines whether the RenderedByScriptControl was already rendered during
		/// a normal rendering.
		/// </summary>
		/// <remarks>
		/// The RenderedByScriptControl can be normally rendered by the Render method
		/// even during a CallBack (i.e when all the controls of an AjaxPanel is new
		/// and the AjaxPanel renders all of them in a single html rendering).
		/// In this case it's not necessary to send any javascript to the client.
		/// </remarks>
		protected bool IsHtmlRendered
		{
			get { return isHtmlRendered; }
		}

		protected override void OnCallBack(EventArgs e)
		{
			isHtmlRendered = false;

			if ( CallBackHelper.IsCallBackStartup )
			{
				isRenderedOnPage = false;
				currentTagHtml = tagOnPageHtml;
			}

			base.OnCallBack(e);
		}

		protected override void OnPreRender(EventArgs e)
		{
			CallBackHelper.RegisterCallBackStartupScript();
			base.OnPreRender (e);
		}

		/// <summary>
		/// Normal rendering.
		/// </summary>
		/// <remarks>
		/// It raises the RenderStart and RenderEnd events. If the rendering is not
		/// aborted during the RenderStart event, it renders the control and sets
		/// the isHtmlRendered variable to true.
		/// </remarks>
		/// <param name="writer"></param>
		protected override void Render(HtmlTextWriter writer)
		{
			RenderStartEventArgs eventArgs = new RenderStartEventArgs(writer);
			OnRenderStart(eventArgs);

			if (!eventArgs.AbortRendering)
			{
				base.Render (writer);
				isHtmlRendered = true;
				isRenderedOnPage = true;
			}

			OnRenderEnd(EventArgs.Empty);
		}

		/// <summary>
		/// It stores the tag html for later checking.
		/// </summary>
		/// <param name="writer"></param>
		public override void RenderBeginTag(HtmlTextWriter writer)
		{
			System.Text.StringBuilder sb = new System.Text.StringBuilder();
			HtmlTextWriter strwriter = new HtmlTextWriter(new System.IO.StringWriter(sb));
			base.RenderBeginTag (strwriter);
			tagOnPageHtml = currentTagHtml = sb.ToString();

			base.RenderBeginTag (writer);
		}

		/// <summary>
		/// Called by AjaxHttpModule
		/// </summary>
		public virtual void RaisePreRenderByScriptEvent()
		{
			OnPreRenderByScript (EventArgs.Empty);
		}

		/// <summary>
		/// If the tag html of the control is changed, send the attributes using javascript.
		/// </summary>
		/// <returns>True if rendering by script was needed, False if it was not</returns>
		public virtual bool RenderByScript()
		{
			// If there was a normal rendering, javascript is not needed
			if (IsHtmlRendered)
				return false;

			if ( ! IsRenderedOnPage )
			{
				Control con = FindNextVisibleSibling();
				CallBackHelper.WriteAddElementScript (Parent.ClientID, this.GetTagName(), this.ClientID, "", (con == null) ? null : con.ClientID);
			}

			System.Text.StringBuilder sb = new System.Text.StringBuilder();
			HtmlTextWriter strwriter = new HtmlTextWriter(new System.IO.StringWriter(sb));

			// Take care of the tag of the control

			base.RenderBeginTag (strwriter);
			string html = sb.ToString();
			sb.Length = 0;

			if(currentTagHtml != html)
			{
				CallBackHelper.WriteSetAttributesOfControl (ClientID, FormatAttributes(html));
				currentTagHtml = html;
			}

			isRenderedOnPage = true;

			return true;
		}

		protected virtual void OnRenderStart(RenderStartEventArgs eventArgs)
		{
			if (RenderStart != null)
				RenderStart(this, eventArgs);
		}
		
		protected virtual void OnRenderEnd(EventArgs e)
		{
			if (RenderEnd != null)
				RenderEnd(this, e);
		}
		
		protected virtual void OnPreRenderByScript(EventArgs e)
		{
			if (PreRenderByScript != null)
				PreRenderByScript(this, e);
		}

		/// <summary>
		/// Finds the next visible control of the control collection of this
		/// control's parent that has its ID attribute set.
		/// </summary>
		/// <returns></returns>
		private Control FindNextVisibleSibling()
		{
			for (int i=Parent.Controls.IndexOf(this) + 1; i < Parent.Controls.Count; i++)
			{
				Control con = Parent.Controls[i];

				if (con.Visible && con.ID != null)
					return con;
			}

			return null;
		}							

		/// <summary>
		/// It finds the attributes from the html of the control's tag, and formats them
		/// so that they are send by CallBackHelper.WriteSetAttributesOfControl method.
		/// </summary>
		/// <param name="html">The html tag of the control</param>
		/// <returns></returns>
		private string FormatAttributes(string html)
		{
			System.Text.StringBuilder sb = new System.Text.StringBuilder(html);
			System.Text.StringBuilder attribs = new System.Text.StringBuilder();
			int mode = 0;

			for (int i=0; i < sb.Length; i++)
			{
				char ch = sb[i];
				if (ch == '>') break;

				switch (mode)
				{
					case 0:
						if (ch == ' ')	mode++;
						break;
					case 1:
					switch (ch)
					{
						case '\"':
							mode++;
							break;
						case ' ':
							attribs.Append ('|');
							break;
						default:
							attribs.Append (ch);
							break;
					}
						break;
					case 2:
						if (ch == '\"')
							mode--;
						else
							attribs.Append (ch);
						break;
				}
			}

			return attribs.ToString();
		}
	}
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Web Developer
Greece Greece
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions