PageRequestManager.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ DotNET / DotNET / 8.0 / untmp / Orcas / RTM / ndp / fx / src / xsp / System / Web / Extensions / ui / PageRequestManager.cs / 2 / PageRequestManager.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Web.UI { 
    using System; 
    using System.Collections;
    using System.Collections.Generic; 
    using System.Collections.Specialized;
    using System.Diagnostics;
    using System.Globalization;
    using System.IO; 
    using System.Security;
    using System.Text; 
    using System.Web; 
    using System.Web.Configuration;
    using System.Web.UI; 
    using System.Web.UI.HtmlControls;
    using System.Web.Resources;
    using System.Web.Script.Serialization;
    using System.Reflection; 
    using System.Security.Permissions;
 
    internal sealed class PageRequestManager { 

        // Type tokens for partial rendering format 
        internal const string PageRedirectToken = "pageRedirect";
        internal const string HiddenFieldToken = "hiddenField";
        private const string AsyncPostBackControlIDsToken = "asyncPostBackControlIDs";
        private const string PostBackControlIDsToken = "postBackControlIDs"; 
        private const string UpdatePanelIDsToken = "updatePanelIDs";
        private const string AsyncPostBackTimeoutToken = "asyncPostBackTimeout"; 
        private const string ChildUpdatePanelIDsToken = "childUpdatePanelIDs"; 
        private const string UpdatePanelsToRefreshToken = "panelsToRefreshIDs";
        private const string FormActionToken = "formAction"; 
        private const string DataItemToken = "dataItem";
        private const string DataItemJsonToken = "dataItemJson";
        internal const string ArrayDeclarationToken = "arrayDeclaration";
        internal const string ExpandoToken = "expando"; 
        internal const string OnSubmitToken = "onSubmit";
        internal const string ScriptBlockToken = "scriptBlock"; 
        internal const string ScriptStartupBlockToken = "scriptStartupBlock"; 
        internal const string ScriptDisposeToken = "scriptDispose";
        internal const string ErrorToken = "error"; 
        internal const string AsyncPostBackErrorKey = "System.Web.UI.PageRequestManager:AsyncPostBackError";
        internal const string AsyncPostBackErrorMessageKey = "System.Web.UI.PageRequestManager:AsyncPostBackErrorMessage";
        internal const string AsyncPostBackErrorHttpCodeKey = "System.Web.UI.PageRequestManager:AsyncPostBackErrorHttpCode";
        private const string PageTitleToken = "pageTitle"; 
        private const string FocusToken = "focus";
 
        private const char LengthEncodeDelimiter = '|'; 
        private static readonly Version MinimumW3CDomVersion = new Version(1, 0);
        private static readonly Version MinimumEcmaScriptVersion = new Version(1, 0); 

        private ScriptManager _owner;

        private List _allUpdatePanels; 
        private List _updatePanelsToRefresh;
        private List _childUpdatePanelsToRefresh; 
        private List _asyncPostBackControls; 
        private List _postBackControls;
        private ScriptDataItemCollection _scriptDataItems; 
        private string _updatePanelRequiresUpdate;
        private HtmlTextWriter _updatePanelWriter;
        private bool _panelsInitialized;
        private string _asyncPostBackSourceElementID; 

        // Stolen from Whidbey Page.cs for focus support 
        private static readonly Version FocusMinimumEcmaVersion = new Version("1.4"); 
        private static readonly Version FocusMinimumJScriptVersion = new Version("3.0");
        private string _focusedControlID; 
        private Control _focusedControl;
        private bool _requireFocusScript;

        public PageRequestManager(ScriptManager owner) { 
            Debug.Assert(owner != null);
            _owner = owner; 
        } 

 
        public string AsyncPostBackSourceElementID {
            get {
                if (_asyncPostBackSourceElementID == null) {
                    return String.Empty; 
                }
                return _asyncPostBackSourceElementID; 
            } 
        }
 
        // Stolen from Whidbey Page.cs
        private bool ClientSupportsFocus {
            get {
                IHttpBrowserCapabilities browser = _owner.IPage.Request.Browser; 
                return
                    (browser.EcmaScriptVersion >= FocusMinimumEcmaVersion) || 
                    (browser.JScriptVersion >= FocusMinimumJScriptVersion); 
            }
        } 

        private bool EnableLegacyRendering {
            get {
                return _owner.EnableLegacyRendering; 
            }
        } 
 
        [SecurityCritical()]
        [SecurityTreatAsSafe()] 
        private bool CustomErrorsSectionHasRedirect(int httpCode) {
            bool hasRedirect = (_owner.CustomErrorsSection.DefaultRedirect != null);
            if (!hasRedirect) {
                if (_owner.CustomErrorsSection.Errors != null) { 
                    foreach (CustomError error in _owner.CustomErrorsSection.Errors) {
                        if (error.StatusCode == httpCode) { 
                            hasRedirect = true; 
                            break;
                        } 
                    }
                }
            }
            return hasRedirect; 
        }
 
        // Optimized version of EncodeString that writes directly to a writer. This 
        // eliminates the need to create several copies of the same string as well
        // as a StringBuilder. 
        internal static void EncodeString(TextWriter writer, string type, string id, string content) {
            Debug.Assert(!String.IsNullOrEmpty(type), "Type should always be specified");
            if (id == null) {
                id = String.Empty; 
            }
            if (content == null) { 
                content = String.Empty; 
            }
            Debug.Assert(type.IndexOf(LengthEncodeDelimiter) == -1, "Should not be a " + LengthEncodeDelimiter + " in type"); 
            Debug.Assert(id.IndexOf(LengthEncodeDelimiter) == -1, "Should not be a " + LengthEncodeDelimiter + " in id");

            // len|type|id|content|
            //             -------   len 

            writer.Write(content.Length.ToString(CultureInfo.InvariantCulture)); 
            writer.Write(LengthEncodeDelimiter); 
            writer.Write(type);
            writer.Write(LengthEncodeDelimiter); 
            writer.Write(id);
            writer.Write(LengthEncodeDelimiter);
            // DevDiv 75383: We used to escape null characters from the content, but this had a non trivial hit on perf
            // They were escaped because XMLHttpRequest in IE truncates content after a null character. 
            // However, when HTML contains a null character, subsequent content is truncated anyway, so the value of escaping nulls
            // in the first place is not clear and it was decided it is not worth the perf hit. 
            writer.Write(content); 
            writer.Write(LengthEncodeDelimiter);
        } 

        private string GetAllUpdatePanelIDs() {
            return GetUpdatePanelIDsFromList(_allUpdatePanels, true);
        } 

        private string GetAsyncPostBackControlIDs(bool includeQuotes) { 
            return GetControlIDsFromList(_asyncPostBackControls, includeQuotes); 
        }
 
        private string GetChildUpdatePanelIDs() {
            return GetUpdatePanelIDsFromList(_childUpdatePanelsToRefresh, false);
        }
 
        private static string GetControlIDsFromList(List list, bool includeQuotes) {
            if (list != null && list.Count > 0) { 
                StringBuilder asyncPostBackControlIDs = new StringBuilder(); 
                bool first = true;
                for (int i = 0; i < list.Count; i++) { 
                    if (!list[i].Visible) {
                        // If the panel isn't visible, the client doesn't need to know about it
                        continue;
                    } 
                    if (!first) {
                        asyncPostBackControlIDs.Append(','); 
                    } 
                    first = false;
                    if (includeQuotes) { 
                        asyncPostBackControlIDs.Append('\'');
                    }
                    asyncPostBackControlIDs.Append(list[i].UniqueID);
                    if (includeQuotes) { 
                        asyncPostBackControlIDs.Append('\'');
                    } 
                } 
                return asyncPostBackControlIDs.ToString();
            } 
            return String.Empty;
        }

        private static Exception GetControlRegistrationException(Control control) { 
            if (control == null) {
                return new ArgumentNullException("control"); 
            } 
            if (control is Page) {
                return new ArgumentException(AtlasWeb.ScriptManager_CannotRegisterPage); 
            }
            if (!(control is INamingContainer) &&
                !(control is IPostBackDataHandler) &&
                !(control is IPostBackEventHandler)) { 
                return new ArgumentException(String.Format(CultureInfo.InvariantCulture, AtlasWeb.ScriptManager_InvalidControlRegistration, control.ID));
            } 
            return null; 
        }
 
        // This code is roughly stolen from HttpException.GetHttpCodeForException()
        private static int GetHttpCodeForException(Exception e) {
            HttpException he = e as HttpException;
            if (he != null) { 
                return he.GetHttpCode();
            } 
            else if (e is UnauthorizedAccessException) { 
                return 401;
            } 
            else if (e is PathTooLongException) {
                return 414;
            }
 
            // If there is an inner exception, try to get the code from it
            if (e.InnerException != null) 
                return GetHttpCodeForException(e.InnerException); 

            // If all else fails, use 500 
            return 500;
        }

        private string GetPostBackControlIDs(bool includeQuotes) { 
            return GetControlIDsFromList(_postBackControls, includeQuotes);
        } 
 
        private string GetRefreshingUpdatePanelIDs() {
            return GetUpdatePanelIDsFromList(_updatePanelsToRefresh, false); 
        }

        private static string GetUpdatePanelIDsFromList(List list, bool includeChildrenAsTriggersPrefix) {
            if (list != null && list.Count > 0) { 
                StringBuilder updatePanelIDs = new StringBuilder();
                bool first = true; 
                for (int i = 0; i < list.Count; i++) { 
                    if (!list[i].Visible) {
                        // If the panel isn't visible, the client doesn't need to know about it 
                        continue;
                    }
                    if (!first) {
                        updatePanelIDs.Append(','); 
                    }
                    first = false; 
                    // We send down the UniqueID instead of the ClientID because 
                    // we need both versions on the client. You can convert from
                    // UniqueID to ClientID, but not back. 

                    // We also send down a bool indicating whether the children of
                    // the panel count as triggers or not.
                    if (includeChildrenAsTriggersPrefix) { 
                        updatePanelIDs.Append(list[i].ChildrenAsTriggers ? 't' : 'f');
                    } 
                    updatePanelIDs.Append(list[i].UniqueID); 
                }
                return updatePanelIDs.ToString(); 
            }
            return String.Empty;
        }
 
        internal static bool IsAsyncPostBackRequest(NameValueCollection headers) {
            // Detect the header for async postbacks. A header can appear 
            // multiple times, and each header entry can contain a comma-separated 
            // list of values. ASP.NET doesn't split the comma-separated values for
            // us so we have to do it. 

            // We used to use the Pragma header but some browsers, such as Opera,
            // do not support sending it through XMLHttpRequest. Instead we use a
            // custom header, X-MicrosoftAjax. 
            string[] headerValues = headers.GetValues("X-MicrosoftAjax");
            if (headerValues != null) { 
                for (int i = 0; i < headerValues.Length; i++) { 
                    string[] headerContents = headerValues[i].Split(',');
                    for (int j = 0; j < headerContents.Length; j++) { 
                        if (headerContents[j].Trim() == "Delta=true") {
                            return true;
                        }
                    } 
                }
            } 
            return false; 
        }
 
        internal void LoadPostData(string postDataKey, NameValueCollection postCollection) {
            // Check if the async postback was caused by a specific panel, and if so, force
            // that panel to update, regardless of whether it had any explicit triggers, etc.
            // If the post back data starts with the ScriptManager's UniqueID that means the 
            // async postback was caused by a control outside of an UpdatePanel, and the rest
            // of the string is the UniqueID of that control. 
            string postBackSourceInfo = postCollection[postDataKey]; 
            if (postBackSourceInfo != null) {
                string postBackTarget; // The target of the postback - either the ScriptManager or an UpdatePanel 

                int indexOfPipe = postBackSourceInfo.IndexOf('|');
                if (indexOfPipe != -1) {
                    // We have a target and source element 
                    postBackTarget = postBackSourceInfo.Substring(0, indexOfPipe);
                    _asyncPostBackSourceElementID = postBackSourceInfo.Substring(indexOfPipe + 1); 
                } 
                else {
                    // We only have a target 
                    postBackTarget = postBackSourceInfo;
                    _asyncPostBackSourceElementID = String.Empty;
                }
 
                if (postBackTarget != _owner.UniqueID) {
                    _updatePanelRequiresUpdate = postBackTarget; 
                } 
            }
 
            // Initialize all UpdatePanels (and their triggers, specifically) so that
            // they can hook events, etc. before other controls can process their
            // own post data.
            // LoadPostData on ScriptManager only gets called during async posts, and 
            // is guaranteed to be called before any other controls have a chance to
            // process their post data. 
            // During regular posts the UpdatePanel initializes itself in OnLoad. 
            if ((_allUpdatePanels != null) && (_allUpdatePanels.Count != 0)) {
                foreach (UpdatePanel panel in _allUpdatePanels) { 
                    panel.Initialize();
                }
            }
 
            _panelsInitialized = true;
        } 
 
        internal void OnInit() {
            // Check if the browser supports partial rendering. We only do the check 
            // if the user hasn't already forced the feature to be on or off explicitly.
            if (_owner.EnablePartialRendering && !_owner._supportsPartialRenderingSetByUser) {
                IHttpBrowserCapabilities browser = _owner.IPage.Request.Browser;
                // There is no browser cap that directly tells us whether the browser 
                // supports XmlHttpRequest so we use the next best capability, which is
                // the SupportsCallback property. 
                // Checking the other properties helps exclude agents such as crawlers. 
                bool supportsPartialRendering =
                    (browser.W3CDomVersion >= MinimumW3CDomVersion) && 
                    (browser.EcmaScriptVersion >= MinimumEcmaScriptVersion) &&
                    browser.SupportsCallback;
                if (supportsPartialRendering) {
                    // If we still think we want to support it, now do a more expensive 
                    // check for XHTML legacy rendering support.
                    supportsPartialRendering = !EnableLegacyRendering; 
                } 
                _owner.SupportsPartialRendering = supportsPartialRendering;
            } 

            if (_owner.IsInAsyncPostBack) {
                _owner.IPage.Error += OnPageError;
            } 
        }
 
        private void OnPageError(object sender, EventArgs e) { 
            Exception ex = _owner.IPage.Server.GetLastError();
            _owner.OnAsyncPostBackError(new AsyncPostBackErrorEventArgs(ex)); 

            string errorMessage = _owner.AsyncPostBackErrorMessage;
            if (String.IsNullOrEmpty(errorMessage) && !_owner.Control.Context.IsCustomErrorEnabled) {
                // Only use the exception's message if we're not doing custom errors 
                errorMessage = ex.Message;
            } 
 
            int httpCode = GetHttpCodeForException(ex);
 
            bool showAsyncErrorMessage = false;

            if (_owner.AllowCustomErrorsRedirect && _owner.Control.Context.IsCustomErrorEnabled) {
                // Figure out if there's going to be a redirect for this error 
                bool hasRedirect = CustomErrorsSectionHasRedirect(httpCode);
                if (!hasRedirect) { 
                    // If there's no redirect, we need to send back the error message 
                    showAsyncErrorMessage = true;
                } 
                // If there was a redirect we do nothing since ASP.NET will automatically
                // redirect the user to the error page anyway. This way we don't have to
                // worry about how to resolve the paths from config.
            } 
            else {
                // If we're not going to use custom errors, just send back the error message 
                showAsyncErrorMessage = true; 
            }
 
            if (showAsyncErrorMessage) {
                IDictionary items = _owner.Control.Context.Items;
                items[AsyncPostBackErrorKey] = true;
                items[AsyncPostBackErrorMessageKey] = errorMessage; 
                items[AsyncPostBackErrorHttpCodeKey] = httpCode;
            } 
        } 

        internal void OnPreRender() { 
            _owner.IPage.SetRenderMethodDelegate(RenderPageCallback);
        }

        private void ProcessFocus(HtmlTextWriter writer) { 
            // Roughly stolen from Whidbey Page.cs
            if (_requireFocusScript) { 
                Debug.Assert(ClientSupportsFocus, "If ClientSupportsFocus is false then we never should have set _requireFocusScript to true."); 
                string focusedControlId = String.Empty;
 
                // Someone calling SetFocus(controlId) has the most precedent
                if (!String.IsNullOrEmpty(_focusedControlID)) {
                    focusedControlId = _focusedControlID;
                } 
                else {
                    if (_focusedControl != null && _focusedControl.Visible) { 
                        focusedControlId = _focusedControl.ClientID; 
                    }
                } 
                if (focusedControlId.Length > 0) {
                    // Register focus script library
                    string focusResourceUrl = _owner.GetScriptResourceUrl("Focus.js", typeof(HtmlForm).Assembly);
                    EncodeString(writer, ScriptBlockToken, "ScriptPath", focusResourceUrl); 

                    // Send the target control ID to the client 
                    EncodeString(writer, FocusToken, String.Empty, focusedControlId); 
                }
            } 
        }

        private void ProcessScriptRegistration(HtmlTextWriter writer) {
            _owner.ScriptRegistration.RenderActiveArrayDeclarations(_updatePanelsToRefresh, writer); 
            _owner.ScriptRegistration.RenderActiveScripts(_updatePanelsToRefresh, writer);
            _owner.ScriptRegistration.RenderActiveSubmitStatements(_updatePanelsToRefresh, writer); 
            _owner.ScriptRegistration.RenderActiveExpandos(_updatePanelsToRefresh, writer); 
            _owner.ScriptRegistration.RenderActiveHiddenFields(_updatePanelsToRefresh, writer);
            _owner.ScriptRegistration.RenderActiveScriptDisposes(_updatePanelsToRefresh, writer); 
        }

        private void ProcessUpdatePanels() {
            Debug.Assert(_owner.IsInAsyncPostBack); 
            Debug.Assert(_updatePanelsToRefresh == null);
 
            if (_allUpdatePanels != null) { 
                _updatePanelsToRefresh = new List(_allUpdatePanels.Count);
                _childUpdatePanelsToRefresh = new List(_allUpdatePanels.Count); 

                // Process the UpdatePanels to determine which are to be set in
                // partial rendering mode.
 
                // We need to process the list such that parent UpdatePanels are
                // evaluated first. A child UpdatePanel inside a parent that is being 
                // updated should not be considered in partial rendering mode. 
                // Ordinarily child controls get initialized first before their parents
                // so you'd expect the list to be in reverse order, but this isn't the case. 
                // UpdatePanels instantiate their templates in their OnInit, so a child
                // UpdatePanel only exists in the control tree after the parent has been
                // initialized.
 
                HtmlForm form = _owner.Page.Form;
 
                for (int i = 0; i < _allUpdatePanels.Count; i++) { 
                    UpdatePanel panel = _allUpdatePanels[i];
 
                    // Check whether the panel thinks it wants to update. Possible reasons
                    // a panel might be updating:
                    // - Postback data indicates the postback came from within the panel
                    // - Explicit call to panel.Update() 
                    // - Panel UpdateMode set to Always
                    // - Trigger fired (not yet implemented) 
 
                    bool panelUpdatedByClient =
                        _updatePanelRequiresUpdate != null && 
                        String.Equals(panel.UniqueID, _updatePanelRequiresUpdate, StringComparison.Ordinal);
                    bool requiresUpdate = panelUpdatedByClient || panel.RequiresUpdate;

                    // Check and see if a parent panel will take update this panel, whether 
                    // this panel wants to update or not. If so, then this panel doesn't need
                    // to be in update mode since it will get included in the rendering 
                    // by its parent anyway. 
                    // If this parent doesn't want to update then we don't need to do any
                    // additional checks because whether it renders depends entirely on 
                    // whether the parent wants to render.
                    Control parent = panel.Parent;
                    while (parent != form) {
                        UpdatePanel parentUpdatePanel = parent as UpdatePanel; 
                        if ((parentUpdatePanel != null) &&
                            (_updatePanelsToRefresh.Contains(parentUpdatePanel) || _childUpdatePanelsToRefresh.Contains(parentUpdatePanel))) { 
                            // This panel is inside another UpdatePanel that is being 
                            // rendered, so it should render in normal mode.
                            requiresUpdate = false; 
                            _childUpdatePanelsToRefresh.Add(panel);
                            break;
                        }
 
                        parent = parent.Parent;
 
                        if (parent == null) { 
                            // This UpdatePanel was not inside an HtmlForm
                            // This really shouldn't happen, because the UpdatePanel would have thrown 
                            // an exception on the initial GET request that it should be inside a form,
                            // so we'll just ignore it now...
                            requiresUpdate = false;
                            break; 
                        }
                    } 
 
                    if (requiresUpdate) {
                        panel.SetAsyncPostBackMode(true); 
                        _updatePanelsToRefresh.Add(panel);
                    }
                    else {
                        panel.SetAsyncPostBackMode(false); 
                    }
                } 
            } 
        }
 
        public void RegisterAsyncPostBackControl(Control control) {
            Exception ex = GetControlRegistrationException(control);
            if (ex != null) {
                throw ex; 
            }
            if (_postBackControls != null && _postBackControls.Contains(control)) { 
                throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, AtlasWeb.ScriptManager_CannotRegisterBothPostBacks, control.ID)); 
            }
            if (_asyncPostBackControls == null) { 
                _asyncPostBackControls = new List();
            }
            // It is acceptable to register the same control twice since the same
            // control might be referred to by more than one trigger. 
            if (!_asyncPostBackControls.Contains(control)) {
                _asyncPostBackControls.Add(control); 
            } 
        }
 
        public void RegisterDataItem(Control control, string dataItem, bool isJsonSerialized) {
            if (control == null) {
                throw new ArgumentNullException("control");
            } 
            if (!_owner.IsInAsyncPostBack) {
                throw new InvalidOperationException(AtlasWeb.PageRequestManager_RegisterDataItemInNonAsyncRequest); 
            } 
            if (_scriptDataItems == null) {
                _scriptDataItems = new ScriptDataItemCollection(); 
            }
            else {
                if (_scriptDataItems.ContainsControl(control)) {
                    throw new ArgumentException( 
                        String.Format(
                            CultureInfo.InvariantCulture, 
                            AtlasWeb.PageRequestManager_RegisterDataItemTwice, 
                            control.ID),
                        "control"); 
                }
            }
            _scriptDataItems.Add(new ScriptDataItem(control, dataItem, isJsonSerialized));
        } 

        private void RegisterFocusScript() { 
            if (ClientSupportsFocus && !_requireFocusScript) { 
                _requireFocusScript = true;
            } 
        }

        public void RegisterPostBackControl(Control control) {
            Exception ex = GetControlRegistrationException(control); 
            if (ex != null) {
                throw ex; 
            } 
            if (_asyncPostBackControls != null && _asyncPostBackControls.Contains(control)) {
                throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, AtlasWeb.ScriptManager_CannotRegisterBothPostBacks, control.ID)); 
            }
            if (_postBackControls == null) {
                _postBackControls = new List();
            } 
            // It is acceptable to register the same control twice since the same
            // control might be referred to by more than one trigger. 
            if (!_postBackControls.Contains(control)) { 
                _postBackControls.Add(control);
            } 
        }

        internal void RegisterUpdatePanel(UpdatePanel updatePanel) {
            Debug.Assert(updatePanel != null); 
            if (_allUpdatePanels == null) {
                _allUpdatePanels = new List(); 
            } 
            Debug.Assert(!_allUpdatePanels.Contains(updatePanel),
                String.Format(CultureInfo.InvariantCulture, "The UpdatePanel with ID '{0}' has already been registered with the ScriptManager.", updatePanel.ID)); 
            _allUpdatePanels.Add(updatePanel);

            if (_panelsInitialized) {
                // Do catch-up for panels that may have been added later in 
                // the lifecycle during an async post.
                Debug.Assert(_owner.IsInAsyncPostBack, "Catch-up initialization should only be done in async posts."); 
                updatePanel.Initialize(); 
            }
        } 

        internal void Render(HtmlTextWriter writer) {
            if (!((IControl)_owner).DesignMode && !_owner.IsInAsyncPostBack && _owner.SupportsPartialRendering) {
                _owner.IPage.VerifyRenderingInServerForm(_owner); 
                RenderPageRequestManagerScript(writer);
            } 
        } 

        private void RenderFormCallback(HtmlTextWriter writer, Control containerControl) { 

            Debug.Assert(_updatePanelWriter != null, "_updatePanelWriter should be set by RenderPageCallback before RenderFormCallback is called.");

            ParserStringWriter parserWriter = writer.InnerWriter as ParserStringWriter; 
            // Disable parsing while rendering UpdatePanels
            parserWriter.ParseWrites = false; 
 

            // Suppress rendering of default content; instead just render out 
            // update panels
            if (_updatePanelsToRefresh != null) {
                foreach (UpdatePanel panel in _updatePanelsToRefresh) {
                    if (panel.Visible) { 
                        // Write UpdatePanels to the response's writer; the writer passed in is a
                        // dummy parserWriter.  It will contain hidden fields that are written to 
                        // the response's writer later in RenderPageCallback. 
                        panel.RenderControl(_updatePanelWriter);
                    } 
                }
            }

            IPage page = _owner.IPage; 
            if (page.EnableEventValidation) {
                // If the page has event validation turned on, we need to run through 
                // the render logic for the rest of the page as well. However, the 
                // rendering is essentially ignored.
                // UpdatePanels that were already rendered above do not re-render by checking 
                // a flag whether they already rendered.

                //
 

 
 

                // DevDiv 55447: Do not use Response.Flush and a NullStream to prevent Response.Writes 
                // from being written to the output stream, as calling Response.Flush causes headers to
                // be written. This prevents cookies from being issued after this, for example.
                // Instead, use a NullWriter that ignores Writes. We can change the writer used by HttpResponse
                // using the internal SwitchWriter method. 
                // We must do this since data written via Response.Write will make the partial update
                // response invalid. 
 
                TextWriter oldWriter = null;
                bool writerSwitched = false; 
                try {
                    // beginning of possible direct Response.Writes
                    oldWriter = page.Response.SwitchWriter(TextWriter.Null);
                    // if we cant switch the writer for some reason we need to know not to switch it back again in the finally block 
                    // writerSwitched will be false
                    writerSwitched = true; 
 
                    // nullHtmlWriter captures any writes by controls to the textwriter they are passed.
                    // Note that we could possibly just let null TextWriter we switched catch this data, but this 
                    // is more efficient.
                    HtmlTextWriter nullHtmlWriter = new HtmlTextWriter(TextWriter.Null);
                    foreach (Control control in containerControl.Controls) {
                        control.RenderControl(nullHtmlWriter); 
                    }
                } 
                finally { 
                    // end of possible direct Response.Writes
                    if (writerSwitched) { 
                        page.Response.SwitchWriter(oldWriter);
                    }
                }
            } 

            // Re-enable parsing now that the form is done rendering its content 
            parserWriter.ParseWrites = true; 
        }
 
        private void RenderPageCallback(HtmlTextWriter writer, Control pageControl) {
            ProcessUpdatePanels();

            // Although we could use the pageControl parameter it's hard to write 
            // unit tests for it. Instead we just use our own page, which is the
            // same instance anyway (but easier to test with). 
 
            IHttpResponse response = _owner.IPage.Response;
 
            response.ContentType = "text/plain";
            response.Cache.SetNoServerCaching();

            // Render the form. It will render its tag, hidden fields, etc. 
            // and then call our render method delegate, which will in turn render
            // all the UpdatePanels 
            IHtmlForm formControl = _owner.IPage.Form; 
            formControl.SetRenderMethodDelegate(RenderFormCallback);
 
            // Let updatePanels write directly to Response
            _updatePanelWriter = writer;

            // Let form header/footer write to a parser 
            ParserStringWriter parserWriter = new ParserStringWriter();
            ParserHtmlTextWriter formWriter = new ParserHtmlTextWriter(parserWriter); 
            parserWriter.ParseWrites = true; 
            formControl.RenderControl(formWriter);
            parserWriter.ParseWrites = false; 

            // Write out built-in ASP.NET hidden fields
            // Hidden fields registered by partial-rendering compatible controls will be
            // rendered by ProcessScriptRegistration(). 
            foreach (KeyValuePair hiddenField in parserWriter.HiddenFields) {
                if (ControlUtil.IsBuiltInHiddenField(hiddenField.Key)) { 
                    EncodeString(writer, HiddenFieldToken, hiddenField.Key, hiddenField.Value); 
                }
            } 

            // Write out PageRequestManager settings that can change during an async postback.
            // This is required for dynamic UpdatePanels since the list of panels could
            // change. 
            EncodeString(writer, AsyncPostBackControlIDsToken, String.Empty, GetAsyncPostBackControlIDs(false));
            EncodeString(writer, PostBackControlIDsToken, String.Empty, GetPostBackControlIDs(false)); 
            EncodeString(writer, UpdatePanelIDsToken, String.Empty, GetAllUpdatePanelIDs()); 
            EncodeString(writer, ChildUpdatePanelIDsToken, String.Empty, GetChildUpdatePanelIDs());
            EncodeString(writer, UpdatePanelsToRefreshToken, String.Empty, GetRefreshingUpdatePanelIDs()); 
            EncodeString(writer, AsyncPostBackTimeoutToken, String.Empty, _owner.AsyncPostBackTimeout.ToString(CultureInfo.InvariantCulture));
            if (formWriter.FormAction != null) {
                EncodeString(writer, FormActionToken, String.Empty, formWriter.FormAction);
            } 
            if (_owner.IPage.Header != null) {
                string pageTitle = _owner.IPage.Title; 
                if (!String.IsNullOrEmpty(pageTitle)) { 
                    EncodeString(writer, PageTitleToken, String.Empty, pageTitle);
                } 
            }
            RenderDataItems(writer);

            ProcessScriptRegistration(writer); 

            // We process the focus after regular script registrations to 
            // make sure that if it ends up including some script that it 
            // executes last.
            ProcessFocus(writer); 
        }

        private void RenderDataItems(HtmlTextWriter writer) {
            if (_scriptDataItems != null) { 
                foreach (ScriptDataItem dataItem in _scriptDataItems) {
                    EncodeString( 
                        writer, 
                        dataItem.IsJsonSerialized ? DataItemJsonToken : DataItemToken,
                        dataItem.Control.ClientID, 
                        dataItem.DataItem);
                }
            }
        } 

        internal void RenderPageRequestManagerScript(HtmlTextWriter writer) { 
            // 

 



 
            // Script format:
            // 
 
            // Writing directly to the writer is more performant than building
            // up a big string with formatting and then writing it out later. 
 
            writer.Write(@" 
");
        } 

        private static void RenderUpdatePanelIDsFromList(HtmlTextWriter writer, List list) {
            // Writing directly to the writer is more performant than building
            // up a big string with formatting and then writing it out later. 
            if (list != null && list.Count > 0) {
                bool first = true; 
                for (int i = 0; i < list.Count; i++) { 
                    if (!list[i].Visible) {
                        // If the panel isn't visible, the client doesn't need to know about it 
                        continue;
                    }
                    if (!first) {
                        writer.Write(','); 
                    }
                    first = false; 
                    writer.Write('\''); 

                    // We send down the UniqueID instead of the ClientID because 
                    // we need both versions on the client. You can convert from
                    // UniqueID to ClientID, but not back.

                    // We also send down a bool indicating whether the children of 
                    // the panel count as triggers or not.
                    writer.Write(list[i].ChildrenAsTriggers ? 't' : 'f'); 
                    writer.Write(list[i].UniqueID); 

                    writer.Write('\''); 
                }
            }
        }
 
        public void SetFocus(Control control) {
            // We always call the real Page's method at least to do parameter validation 
            _owner.IPage.SetFocus(control); 

            // If it's not async, just let the page do whatever it wants. If we are 
            // in an async post, we need to keep track of what to focus later on.
            if (_owner.IsInAsyncPostBack) {
                _focusedControl = control;
                _focusedControlID = null; 
                RegisterFocusScript();
            } 
        } 

        public void SetFocus(string clientID) { 
            // We always call the real Page's method at least to do parameter validation
            _owner.IPage.SetFocus(clientID);
            SetFocusInternal(clientID);
        } 

        internal void SetFocusInternal(string clientID) { 
            // If it's not async, just let the page do whatever it wants. If we are 
            // in an async post, we need to keep track of what to focus later on.
            if (_owner.IsInAsyncPostBack) { 
                _focusedControlID = clientID.Trim();
                _focusedControl = null;
                RegisterFocusScript();
            } 
        }
 
        internal void UnregisterUpdatePanel(UpdatePanel updatePanel) { 
            Debug.Assert(updatePanel != null);
            if ((_allUpdatePanels == null) || !_allUpdatePanels.Contains(updatePanel)) { 
                throw new ArgumentException(String.Format(CultureInfo.InvariantCulture, AtlasWeb.ScriptManager_UpdatePanelNotRegistered, updatePanel.ID), "updatePanel");
            }
            _allUpdatePanels.Remove(updatePanel);
        } 

        // Specialized parser StringWriter that can detect when ASP.NET renders 
        // hidden field tags. It strips them from the rendering, and 
        // they can later be retrieved via properties. It only parses while
        // ParseWrites is set to true. 
        private sealed class ParserStringWriter : StringWriter {
            private enum ParserState {
                Ready = 0,
                ReadHiddenFieldNameValue = 1, 
                ReadHiddenFieldIdAttribute = 2,
                ReadHiddenFieldIdValue = 3, 
                ReadHiddenFieldValueAttribute = 4, 
                ReadHiddenFieldValueValue = 5,
            } 

            private bool _parseWrites;

            private List _pendingWrites = new List(); 

            private ParserState _parserState = ParserState.Ready; 
            private bool _secondTry; // Indicates that we might be accepting a 2nd write for the same parser state 
            private string _proposedHiddenFieldName = String.Empty;
            private string _matchingHiddenFieldName = String.Empty; 
            private StringBuilder _proposedHiddenFieldValue = new StringBuilder();

            private Dictionary _hiddenFields = new Dictionary();
 
            public ParserStringWriter()
                : base(CultureInfo.CurrentCulture) { 
            } 

            // Gets the name/value pairs for hidden fields parsed from the content. 
            public IDictionary HiddenFields {
                get {
                    return _hiddenFields;
                } 
            }
 
            // Indicates whether calls to Write(string) and WriteLine(string) 
            // should be parsed for specific content.
            public bool ParseWrites { 
                get {
                    return _parseWrites;
                }
                set { 
                    _parseWrites = value;
                } 
            } 

            private void FlushPendingWrites() { 
                if (_pendingWrites.Count > 0) {
                    foreach (string pendingWrite in _pendingWrites) {
                        base.Write(pendingWrite);
                    } 
                    _pendingWrites.Clear();
                } 
            } 

            private void ParseString(string s) { 
                switch (_parserState) {
                    case ParserState.Ready:
                        if (s == "") {
                            _proposedHiddenFieldValue.Append(s); 
                        }
                        else { 
                            // If we get here that means we have a complete hidden field 
                            _pendingWrites.Clear();
                            _hiddenFields.Add(_proposedHiddenFieldName, _proposedHiddenFieldValue.ToString()); 
                            _proposedHiddenFieldName = String.Empty;
                            _matchingHiddenFieldName = String.Empty;
                            _proposedHiddenFieldValue = new StringBuilder();
                            _parserState = ParserState.Ready; 
                        }
                        break; 
 
                    default:
                        _secondTry = false; 
                        FlushPendingWrites();
                        base.Write(s);
                        break;
                } 
            }
 
            public override void Write(string s) { 
                if (!ParseWrites) {
                    base.Write(s); 
                    return;
                }

                ParseString(s); 
            }
 
            public override void WriteLine(string value) { 
                if (!ParseWrites) {
                    base.WriteLine(value); 
                    return;
                }

                // For simplicity's sake, we turn parsed WriteLines into two 
                // calls to our parser.
                ParseString(value); 
                ParseString(NewLine); 
            }
        } 

        private sealed class ParserHtmlTextWriter : HtmlTextWriter {
            private bool _writingForm;
            private string _formAction; 

            public string FormAction { 
                get { 
                    return _formAction;
                } 
            }


            public ParserHtmlTextWriter(TextWriter writer) 
                : base(writer) {
            } 
 

            public override void WriteBeginTag(string tagName) { 
                base.WriteBeginTag(tagName);

                //
                _writingForm = (tagName == "form"); 
            }
 
            public override void WriteAttribute(string name, string value, bool fEncode) { 
                base.WriteAttribute(name, value, fEncode);
 
                //
                if (_writingForm) {
                    if (name == "action") {
                        _formAction = value; 
                    }
                } 
            } 
        }
 
        private sealed class ScriptDataItem {
            private Control _control;
            private string _dataItem;
            private bool _isJsonSerialized; 

            public ScriptDataItem(Control control, string dataItem, bool isJsonSerialized) { 
                _control = control; 
                _dataItem = (dataItem == null) ? String.Empty : dataItem;
                _isJsonSerialized = isJsonSerialized; 
            }

            public Control Control {
                get { 
                    return _control;
                } 
            } 

            public string DataItem { 
                get {
                    return _dataItem;
                }
            } 

            public bool IsJsonSerialized { 
                get { 
                    return _isJsonSerialized;
                } 
            }
        }

        private sealed class ScriptDataItemCollection : List { 
            public bool ContainsControl(Control control) {
                foreach (ScriptDataItem dataItem in this) { 
                    if (dataItem.Control == control) { 
                        return true;
                    } 
                }
                return false;
            }
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.


                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK