From: Argiris K. <be...@us...> - 2005-11-11 06:17:59
|
Update of /cvsroot/magicajax/magicajax/Core/UI/Controls In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv6302/Core/UI/Controls Added Files: AjaxPanel.cs Log Message: Put source files to module 'magicajax' divided in directories Core, Examples, and CustomControls. --- NEW FILE: AjaxPanel.cs --- #region LGPL License /* MagicAjax Library Copyright (C) 2005 MagicAjax Project Team This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #endregion using System; using System.ComponentModel; using System.Collections; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.HtmlControls; namespace MagicAjax.UI.Controls { /// <summary> /// Defines the CallBack connection types. /// </summary> public enum CallBackConnectionType { /// <summary> /// Client will not invoke a CallBack /// </summary> None, /// <summary> /// Asynchronous CallBack /// </summary> Asynchronous, /// <summary> /// Synchronous CallBack /// </summary> Synchronous } /// <summary> /// Works like Panel but the controls it contains are rendered on the page by sending /// javascript to the client. /// </summary> /// <remarks> /// The main control that makes all the hard work for seamless AJAXing. It spots /// controls that are added, removed or altered, and sends the appropriate javascript /// for the refreshing of the page. In case it contains a RenderedByScriptControl, it /// ignores it and lets the RenderedByScriptControl to produce the appropriate javascript /// for its appearance. /// /// If an AjaxPanel (parent) contains another AjaxPanel (child), and a control /// of the child-AjaxPanel is altered, parent-AjaxPanel won't send the entire html /// rendering of the child-AjaxPanel, but instead the child-AjaxPanel will send only /// the html of the altered control. Thus, the size of javascript code that the client /// gets as a response of a callback, is greatly reduced. /// /// It is not necessary to put all the controls of the page inside an AjaxPanel. Only /// the controls that are going to be refreshed on the client using javascript are /// required to be contained within an AjaxPanel. /// </remarks> [Designer("MagicAjax.UI.Design.AjaxPanelDesigner, MagicAjax"), ParseChildrenAttribute(false), PersistChildren(true), ToolboxData("<{0}:AjaxPanel runat=server>AjaxPanel</{0}:AjaxPanel>")] public class AjaxPanel : RenderedByScriptControl, IFormDataLoadedEventHandler, INamingContainer { #region Fields private CallBackConnectionType _callBackConnection = CallBackConnectionType.Asynchronous; private ArrayList _addedControls = new ArrayList(); private ArrayList _removedControls = new ArrayList(); private Hashtable _controlHtmls = new Hashtable(); private NoVerifyRenderingPage _noVerifyPage = new NoVerifyRenderingPage(); private ControlCollectionState _controlState = new ControlCollectionState(); #endregion #region Constructor /// <summary> /// Creates an instance of a RenderedByScriptControl with the Span tagName. /// </summary> /// <remarks> /// AjaxPanel uses Span instead of Div, because ASP.NET translates Div to a /// table if the browser is Firefox. /// </remarks> public AjaxPanel() : base(HtmlTextWriterTag.Span) { } #endregion #region Properties #region CallBackConnection /// <summary> /// Defines the connection type (a/synchronous) that will be utilized for a CallBack. /// Default is CallBackConnectionType.Asynchronous. /// </summary> public CallBackConnectionType CallBackConnection { get { return _callBackConnection; } set { _callBackConnection = value; switch (_callBackConnection) { case CallBackConnectionType.None: this.Attributes.Remove ("CallBack"); break; case CallBackConnectionType.Asynchronous: this.Attributes["CallBack"] = "async"; break; case CallBackConnectionType.Synchronous: this.Attributes["CallBack"] = "sync"; break; } } } #endregion #endregion #region Public Methods #region RaiseFormDataLoadedEvent /// <summary> /// Caches the html rendering of the post data changed controls that belong to /// the AjaxPanel. /// </summary> /// <remarks> /// If a child control is altered by post data, AjaxPanel will send the javascript /// to refresh the control on the page, but it is unnecessary because the control /// on the form doesn't need refreshing. /// This method caches the new html rendering of the controls that received /// new post data so that the AjaxPanel doesn't think that they were altered. /// /// Called by MagicAjaxModule. /// </remarks> public virtual void RaiseFormDataLoadedEvent(MagicAjax.Collections.ReadOnlyArrayList changedControls) { ArrayList dirtyControls = new ArrayList(); for (int i=0; i < changedControls.Count; i++) { Control control = (Control) changedControls[i]; // The top parent of this control that belongs to this AjaxPanel Control panelChild = FindTopPanelChildOfControl(control); if (panelChild != null) { dirtyControls.Add (panelChild); } } if (dirtyControls.Count == 0) return; DisableVerifyRendering (true); System.Text.StringBuilder sb = new System.Text.StringBuilder(); HtmlTextWriter litewriter = new HtmlTextWriter(new System.IO.StringWriter(sb)); for (int i=0; i < dirtyControls.Count; i++) { Control control = (Control) dirtyControls[i]; ExtendedRenderControl (control, litewriter); _controlHtmls[control] = sb.ToString(); sb.Length = 0; } DisableVerifyRendering (false); } #endregion #region Clear /// <summary> /// It removes the child controls of the AjaxPanel without producing the /// appropriate javascript to refresh the page at the client. /// </summary> public void Clear() { Controls.Clear(); _addedControls.Clear(); _removedControls.Clear(); _controlHtmls.Clear(); } #endregion #endregion #region Protected Methods #region override OnLoad protected override void OnLoad(EventArgs e) { base.OnLoad (e); // Enable validation if there is at least one validator. _noVerifyPage.EnableValidation = (Page.Validators.Count > 0); if (this.Attributes["CallBack"] == null) { // To set the appropriate "CallBack" attribute. this.CallBackConnection = this._callBackConnection; } } #endregion #region override AddedControl /// <summary> /// Adds the control to the added controls collection. /// </summary> /// <param name="control"></param> /// <param name="index"></param> protected override void AddedControl(Control control, int index) { _addedControls.Add (control); base.AddedControl (control, index); } #endregion #region override RemovedControl /// <summary> /// Adds the control to the removed controls collection. /// </summary> /// <param name="control"></param> /// <param name="index"></param> protected override void RemovedControl(Control control) { if (_addedControls.Contains(control)) _addedControls.Remove (control); else _removedControls.Add (control); base.RemovedControl (control); } #endregion #region override RenderChildren /// <summary> /// Does a normal rendering of the child controls. /// </summary> /// <remarks> /// Each child control is contained inside a Span tag with unique id so that /// it can be easily manipulated apart from the other child controls. /// </remarks> /// <param name="writer"></param> protected override void RenderChildren(HtmlTextWriter writer) { if ( CallBackHelper.IsCallBack && IsPageNoStoreMode ) { LoadControlState(); DoScriptRendering (writer); } else { DisableVerifyRendering (true); System.Text.StringBuilder sbFull = new System.Text.StringBuilder(); System.Text.StringBuilder sb = new System.Text.StringBuilder(); HtmlTextWriter fullwriter = new HtmlTextWriter(new System.IO.StringWriter(sbFull)); HtmlTextWriter litewriter = new HtmlTextWriter(new System.IO.StringWriter(sb)); for (int i=0; i < Controls.Count; i++) { Control con = Controls[i]; writer.WriteBeginTag ("span"); writer.WriteAttribute ("id", GetAjaxElemID(con)); writer.Write (HtmlTextWriter.TagRightChar); ExtendedRenderControl (con, fullwriter, litewriter); writer.Write (sbFull.ToString()); _controlHtmls[con] = sb.ToString(); sbFull.Length = 0; sb.Length = 0; writer.WriteEndTag ("span"); } DisableVerifyRendering (false); _addedControls.Clear(); _removedControls.Clear(); } if ( IsPageNoStoreMode ) SaveControlState(); } #endregion protected virtual void SaveControlState() { _controlState.SetControlIDs (_controlHtmls); _controlState.Save (this.UniqueID); } protected virtual void LoadControlState() { _controlState = ControlCollectionState.LoadState (this.UniqueID); if (_controlState == null) { // State expired Page.Response.Redirect (Page.Request.RawUrl); } // Find new and previous controls _addedControls.Clear(); _controlHtmls.Clear(); foreach (Control con in this.Controls) { if ( _controlState.ControlIDHtmls.ContainsKey(con.ClientID) ) { _controlHtmls[con] = _controlState.ControlIDHtmls[con.ClientID]; } else { _addedControls.Add (con); } } // Find removed controls _removedControls.Clear(); foreach (string clientID in _controlState.ControlIDHtmls.Keys) { int i; for (i=0; i < this.Controls.Count; i++) { if (this.Controls[i].ClientID == clientID) break; } if (i == this.Controls.Count) { // The control is removed. Add a control with the same id // so that DoScriptRendering "removes" it from page if ( clientID.StartsWith (this.ClientID) ) { Literal lit = new Literal(); lit.ID = clientID.Substring(this.ClientID.Length + 1); _removedControls.Add (lit); } } } } #region override RenderByScript protected override void RenderByScript() { DoScriptRendering (new HtmlTextWriter(System.IO.TextWriter.Null)); } #endregion #region DoScriptRendering /// <summary> /// It scans child controls for added, removed or altered controls and sends /// the appropriate javascript to the client. /// </summary> protected virtual void DoScriptRendering (HtmlTextWriter output) { DisableVerifyRendering (true); System.Text.StringBuilder sb = new System.Text.StringBuilder(); HtmlTextWriter litewriter = new HtmlTextWriter(new System.IO.StringWriter(sb)); System.Text.StringBuilder sbFull = new System.Text.StringBuilder(); HtmlTextWriter fullwriter = new HtmlTextWriter(new System.IO.StringWriter(sbFull)); bool allControlsAreNew = (Controls.Count == _addedControls.Count); if ( ! this.IsRenderedOnPage || allControlsAreNew ) { // Render all the controls in a single html rendering. for (int i=0; i < Controls.Count; i++) { Control con = Controls[i]; fullwriter.WriteBeginTag ("span"); fullwriter.WriteAttribute ("id", GetAjaxElemID(con)); fullwriter.Write (HtmlTextWriter.TagRightChar); ExtendedRenderControl (con, fullwriter, litewriter); fullwriter.WriteEndTag ("span"); _controlHtmls[con] = sb.ToString(); sb.Length = 0; } if (sbFull.Length > 0) { string html = sbFull.ToString(); CallBackHelper.WriteSetHtmlOfElementScript (html, ClientID); output.Write (html); } } else { foreach (Control con in _removedControls) { _controlHtmls.Remove (con); CallBackHelper.WriteRemoveElementScript (ClientID, GetAjaxElemID(con)); } for (int i=0; i < Controls.Count; i++) { Control con = Controls[i]; string html; sbFull.Length = sb.Length = 0; if (_addedControls.Contains(con)) { // It's a new control, create it on the client at the appropriate place. ExtendedRenderControl (con, fullwriter, litewriter); html = sbFull.ToString(); CallBackHelper.WriteAddElementScript (ClientID, "span", GetAjaxElemID(con), html, GetNextExistingElement(i)); output.Write (html); _controlHtmls[con] = sb.ToString(); } else { ExtendedRenderControl (con, output, litewriter); html = sb.ToString(); // If it's html rendering is the same, ignore it. if (html != (string)_controlHtmls[con]) { ExtendedWriteSetHtmlOfElementScript (html, GetAjaxElemID(con)); _controlHtmls[con] = html; } } } } DisableVerifyRendering (false); _addedControls.Clear(); _removedControls.Clear(); } #endregion #endregion #region Private Methods #region ExtendedWriteSetHtmlOfElementScript /// <summary> /// Calls the AJAXCbo.ExtendedSetHtmlOfElementScript function on the client. /// </summary> /// <remarks> /// It's used by RenderByScript method. /// /// AjaxPanel doesn't include RenderedByScriptControl controls in the html rendering, /// to reduce the size of the javascript script sent to clients. /// AJAXCbo.ExtendedSetHtmlOfElementScript finds the RenderedByScriptControl controls /// that are missing from the html rendering, gets them from the page of the client /// and adds them in the appropriate place in the html rendering. /// </remarks> /// <param name="html">The html rendering of the control</param> /// <param name="elementID">The span element id that the control belongs to.</param> private void ExtendedWriteSetHtmlOfElementScript(string html, string elementID) { CallBackHelper.Write( String.Format("AJAXCbo.ExtendedSetHtmlOfElementScript({0},\"{1}\");\r\n", CallBackHelper.EncodeString(html), elementID) ); } #endregion #region ExtendedRenderControl /// <summary> /// It renders the control but without including the html rendering of /// any RenderedByScriptControl controls. /// </summary> /// <remarks> /// It's used by RenderByScript method. /// </remarks> /// <param name="control"></param> /// <param name="litewriter"></param> private void ExtendedRenderControl(Control control, HtmlTextWriter litewriter) { RenderStartEventHandler renderStart = new RenderStartEventHandler(control_RenderStart_Abort); ArrayList list = FindRenderedByScriptControls(control); bool[] visibleProps = new bool[list.Count]; bool[] monitorProps = new bool[list.Count]; for (int i=0; i < list.Count; i++) { RenderedByScriptControl con = (RenderedByScriptControl) list[i]; con.RenderStart += renderStart; visibleProps[i] = con.Visible; monitorProps[i] = con.MonitorVisibilityState; con.MonitorVisibilityState = false; con.Visible = true; } control.RenderControl (litewriter); for (int i=0; i < list.Count; i++) { RenderedByScriptControl con = (RenderedByScriptControl) list[i]; con.RenderStart -= renderStart; con.Visible = visibleProps[i]; con.MonitorVisibilityState = monitorProps[i]; } list.Clear(); } #endregion #region control_RenderStart_Abort /// <summary> /// It's part of the ExtendedRenderControl(Control control) method. It replaces the html rendering /// of a RenderedByScriptControl with a Span tag named "__ajax_rbs". /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void control_RenderStart_Abort(object sender, RenderStartEventArgs e) { RenderedByScriptControl con = (RenderedByScriptControl) sender; e.Writer.Write ("<span id=\"{0}$rbs\" name=\"__ajax_rbs\"><{1} id=\"{0}\"></{1}></span>", con.ClientID, con.GetTagName()); e.AbortRendering = true; } #endregion #region ExtendedRenderControl /// <summary> /// It produces the full rendering of the control and the rendering without /// including the html rendering of any RenderedByScriptControl controls. /// </summary> /// <remarks> /// It's used by RenderByScript method. /// </remarks> /// <param name="control"></param> /// <param name="fullwriter">The writer to use for the full rendering</param> /// <param name="litewriter">The writer to use for the rendering without any RenderedByScriptControl controls</param> private void ExtendedRenderControl(Control control, HtmlTextWriter fullwriter, HtmlTextWriter litewriter) { ExtendedTextWriter extTextWriter = new ExtendedTextWriter(fullwriter, litewriter); RenderStartEventHandler renderStart = new RenderStartEventHandler(extTextWriter.IScriptWriter_RenderStart); EventHandler renderEnd = new EventHandler(extTextWriter.IScriptWriter_RenderEnd); ArrayList list = FindRenderedByScriptControls(control); foreach (RenderedByScriptControl con in list) { con.RenderStart += renderStart; con.RenderEnd += renderEnd; } HtmlTextWriter extwriter = new HtmlTextWriter(extTextWriter); control.RenderControl (extwriter); foreach (RenderedByScriptControl con in list) { con.RenderStart -= renderStart; con.RenderEnd -= renderEnd; } list.Clear(); } #endregion #region DisableVerifyRendering /// <summary> /// Disables the verify rendering in server form check. /// </summary> /// <remarks> /// It disables the verify rendering in server form check of the controls by /// setting NoVerifyRenderingPage to their Page property. It enables it again /// by setting back the Page of AjaxPanel. /// /// See the remarks of the NoVerifyRenderingPage class. /// </remarks> /// <param name="disable">True to set the NoVerifyRenderingPage, false to set the previous page back</param> private void DisableVerifyRendering(bool disable) { // NoVerifyRenderingPage is used even during normal rendering, // so that ':' is used for Unique ID separator instead of '$', // thus javascript to convert the Unique IDs of the page won't be sent // at the first CallBack. // Try not using DisableVerify at all if it's IsPageNoStoreMode if ( this.IsPageNoStoreMode ) return; Page page = (disable) ? _noVerifyPage : this.Page; for (int i=0; i < Controls.Count; i++) { SetPageForControl(page, Controls[i]); } } #endregion #region SetPageForControl /// <summary> /// Sets the supplied page to the Page property of the supplied control and /// its children. /// </summary> /// <remarks> /// If it encounters a RenderedByScriptControl control, it ignores it. /// /// Called by DisableVerifyRendering. /// </remarks> /// <param name="page"></param> /// <param name="control"></param> private void SetPageForControl(Page page, Control control) { if (!(control is RenderedByScriptControl)) { control.Page = page; for (int i=0; i < control.Controls.Count; i++) SetPageForControl (page, control.Controls[i]); } } #endregion #region FindTopPanelChildOfControl /// <summary> /// It returns the top parent control of the supplied control, that is an immediate /// child of this AjaxPanel. If the supplied control is contained inside another /// AjaxPanel, it returns null. /// </summary> /// <remarks> /// Called by RaiseFormDataLoadedEvent. /// </remarks> /// <param name="control"></param> /// <returns></returns> private Control FindTopPanelChildOfControl(Control control) { if (control.Parent == null) return null; else if (control.Parent == this) return control; else if (control.Parent is AjaxPanel) return null; else return FindTopPanelChildOfControl(control.Parent); } #endregion #region FindRenderedByScriptControls /// <summary> /// It returns an ArrayList of all the RenderedByScriptControl controls that /// the supplied control contains. /// </summary> /// <remarks> /// It's used by the ExtendedRenderControl methods. /// </remarks> /// <param name="control"></param> /// <returns></returns> private ArrayList FindRenderedByScriptControls (Control control) { ArrayList list = new ArrayList(); if (control is RenderedByScriptControl) { list.Add (control); } else if (control.Visible) { for (int i=0; i < control.Controls.Count; i++) list.AddRange (FindRenderedByScriptControls(control.Controls[i])); } return list; } #endregion #region GetNextExistingElement /// <summary> /// It returns the next control that is available at the page of the client /// so that a new control can be added before it. /// </summary> /// <remarks> /// It's used by the RenderByScript method. /// /// It checks to see if the html rendering of the control is stored to determine /// whether the control exists on the page or not. /// </remarks> /// <param name="start">The index position to start searching</param> /// <returns></returns> private string GetNextExistingElement(int start) { for (int i=start+1; i < Controls.Count; i++) { Control con = Controls[i]; if (!_addedControls.Contains(con) && _controlHtmls[con] != null) return GetAjaxElemID(con); } return null; } #endregion #region GetAjaxElemID /// <summary> /// It returns the Span tag id that contains the supplied control. /// </summary> /// <param name="control"></param> /// <returns></returns> private static string GetAjaxElemID(Control control) { return control.ClientID + "$ajaxdest"; } #endregion #endregion #region Private Class ControlCollectionState [Serializable] private class ControlCollectionState { private Hashtable _controlIDHtmls; public static ControlCollectionState LoadState(string panelUniqueID) { return (ControlCollectionState) HttpContext.Current.Session[GetPageKey(panelUniqueID)]; } public static string GetPageKey (string panelUniqueID) { return String.Format("__ControlState_{0}_{1}", HttpContext.Current.Request.RawUrl, panelUniqueID); } public Hashtable ControlIDHtmls { get { return _controlIDHtmls; } } public void SetControlIDs (Hashtable controlHtmls) { _controlIDHtmls.Clear(); foreach (Control con in controlHtmls.Keys) _controlIDHtmls.Add (con.ClientID, (string)controlHtmls[con]); } public void Save(string panelUniqueID) { HttpContext.Current.Session[GetPageKey(panelUniqueID)] = this; } public ControlCollectionState() { _controlIDHtmls = new Hashtable(); } } #endregion #region Private Class ExtendedTextWriter /// <summary> /// It enables simultaneous rendering of full rendering and rendering without /// including the html rendering of any RenderedByScriptControl controls. /// </summary> /// <remarks> /// It's used by ExtendedRenderControl(Control, HtmlTextWriter, HtmlTextWriter). /// </remarks> private class ExtendedTextWriter : System.IO.TextWriter { private HtmlTextWriter fullwriter, litewriter; private bool onlyFullRender = false; public override System.Text.Encoding Encoding { get { return null; } } public void IScriptWriter_RenderStart(object sender, RenderStartEventArgs e) { RenderedByScriptControl con = (RenderedByScriptControl) sender; Write ("<span id=\"{0}$rbs\" name=\"__ajax_rbs\">", con.ClientID); onlyFullRender = true; litewriter.Write ("<{0} id=\"{1}\">", con.GetTagName(), con.ClientID); } public void IScriptWriter_RenderEnd(object sender, System.EventArgs e) { RenderedByScriptControl con = (RenderedByScriptControl) sender; litewriter.Write ("</{0}>", con.GetTagName()); onlyFullRender = false; Write ("</span>"); } public ExtendedTextWriter(HtmlTextWriter fullwriter, HtmlTextWriter litewriter) : base(null) { this.fullwriter = fullwriter; this.litewriter = litewriter; } #region Overrides of TextWriter public override void Write(bool value) { fullwriter.Write (value); if (!onlyFullRender) litewriter.Write (value); } public override void Write(char value) { fullwriter.Write (value); if (!onlyFullRender) litewriter.Write (value); } public override void Write(char[] buffer) { fullwriter.Write (buffer); if (!onlyFullRender) litewriter.Write (buffer); } public override void Write(char[] buffer, int index, int count) { fullwriter.Write (buffer, index, count); if (!onlyFullRender) litewriter.Write (buffer, index, count); } public override void Write(decimal value) { fullwriter.Write (value); if (!onlyFullRender) litewriter.Write (value); } public override void Write(double value) { fullwriter.Write (value); if (!onlyFullRender) litewriter.Write (value); } public override void Write(float value) { fullwriter.Write (value); if (!onlyFullRender) litewriter.Write (value); } public override void Write(int value) { fullwriter.Write (value); if (!onlyFullRender) litewriter.Write (value); } public override void Write(long value) { fullwriter.Write (value); if (!onlyFullRender) litewriter.Write (value); } public override void Write(object value) { fullwriter.Write (value); if (!onlyFullRender) litewriter.Write (value); } public override void Write(string format, object arg0) { fullwriter.Write (format, arg0); if (!onlyFullRender) litewriter.Write (format, arg0); } public override void Write(string format, object arg0, object arg1) { fullwriter.Write (format, arg0, arg1); if (!onlyFullRender) litewriter.Write (format, arg0, arg1); } public override void Write(string format, object arg0, object arg1, object arg2) { fullwriter.Write (format, arg0, arg1, arg2); if (!onlyFullRender) litewriter.Write (format, arg0, arg1, arg2); } public override void Write(string format, params object[] arg) { fullwriter.Write (format, arg); if (!onlyFullRender) litewriter.Write (format, arg); } public override void Write(string value) { fullwriter.Write (value); if (!onlyFullRender) litewriter.Write (value); } public override void Write(uint value) { fullwriter.Write (value); if (!onlyFullRender) litewriter.Write (value); } public override void Write(ulong value) { fullwriter.Write (value); if (!onlyFullRender) litewriter.Write (value); } #endregion } #endregion } } |