65.9K
CodeProject 正在变化。 阅读更多。
Home

在自定义控件中支持 ASP.NET AJAX

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.72/5 (21投票s)

2008年6月20日

CPOL

3分钟阅读

viewsIcon

73843

downloadIcon

398

如何更新您的 ASP.NET 自定义控件以使其与 ASP.NET AJAX 正确配合使用。

引言

本文对于任何希望更新其控件以使其与 ASP.NET AJAX 正确配合使用的自定义组件开发人员都将非常有用。首先,我们将描述在进行此类自定义时最常见的问题,然后提出针对这些问题的解决方案。

问题描述

假设您为 ASP.NET 开发了一些可视化控件(例如,具有一些改进的编辑框)。 假设您的控件使用一些客户端脚本来实现其行为中的这些“改进”。

问题是:您的控件是否能很好地与 ASP.NET AJAX 增强功能一起使用? 或者更确切地说:如果有人将您的控件放置在 UpdatePanel 中以启用部分页面更新,一切是否会正常? 总体答案是:。 我将告诉您原因。 如果您的控件使用一些客户端脚本,则应在 PreRender 阶段使用 ClientScriptManager 类的 RegisterClientScriptBlock 方法注册这些脚本。 通常,它看起来类似于以下代码段

protected override void OnPreRender(EventArgs e) {
    base.OnPreRender(e);
    string scriptCode = "<script type=\"text/javascript\">\n<!--\n";
    . . . . . . . . 
    //script code definition
    . . . . . . . . 
    scriptCode += "// -->\n</script>";


    Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "MyScriptName",
        scriptCode);
}

此代码将运行良好,并且您的脚本将在正常页面呈现时包含在页面上。 它甚至可以在 UpdatePanel 内部工作,但前提是您的组件是静态的 - 我的意思是,如果它像往常一样放置在页面上,并在首次页面呈现时创建。 但是,如果您的组件放置在 UpdatePanel 内部并且是响应某些异步更新而动态创建的,则此类代码段将不起作用。

解决方案

为了解决这个问题,我们需要具备以下能力

  • 确定我们的控件是否在 UpdatePanel 内部的能力;
  • 确定当前回发是在部分呈现模式下执行还是普通同步回发的能力;
  • 在异步回发期间注册我们的客户端脚本的方法。

此外,实现所有描述功能的代码不得对 ASP.NET AJAX 程序集(System.Web.Extensions.dll)使用静态链接,否则我们的控件将无法在未安装 ASP.NET AJAX 的网站上运行。

前两个功能很容易实现。 我们只需要查找控件父级并检查其中是否有任何一个控件是 UpdatePanel 控件。 对于第二个任务,我们还需要检查找到的 UpdatePanelIsInPartialRendering 属性。 请记住我们关于静态链接的限制:因此我们可以以通常的方式访问该属性,但需要使用 System.Reflection 命名空间中的类提供的功能。

至于第三个问题(在部分更新时注册客户端脚本):我们应该使用 ScriptManager 对象的 RegisterClientScriptXXX 方法,该方法应包含在每个支持 ASP.NET AJAX 的页面上。 因此,我们需要首先在我们的页面上找到该对象,然后调用必要的方法。 唯一的问题(再次):我们不能直接这样做。 相反,我们应该首先尝试加载 ASP.NET AJAX 程序集,然后找到 ScriptManager 对象并使用反射调用该对象的方法。

因此,这是实现所有描述任务的代码(AJAX 类)。 此类的所有方法都定义为 static,因此我们不需要创建此类实例来调用它们。

using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;

namespace Korzh.WebControls {
    /// <summary>
    /// Represents different procedures for ASP.NET AJAX extentions support
    /// </summary>
    public class Ajax {
        /// <summary>
        /// Determines whether the specified control is inside UpdatePanel
        /// </summary>
        /// <param name="control">The control.</param>
        /// <returns>
        /// <c>true</c> if the specified control is inside UpdatePanel; otherwise,
        /// <c>false</c>.
        /// </returns>
        public static bool IsControlInsideUpdatePanel(Control control) {
            Control parent = control.Parent;
            while (parent != null) {
                if (parent.GetType().FullName.Equals("System.Web.UI.UpdatePanel"))
                    return true;
                parent = parent.Parent;
            }
            return false;            
        }

        /// <summary>
        /// Determines whether the specified control is in partial rendering.
        /// </summary>
        /// <param name="control">The control.</param>
        /// <returns>
        /// <c>true</c> if the specified control is in partial rendering; otherwise,
        /// <c>false</c>.
        /// </returns>
        public static bool IsControlInPartialRendering(Control control) {
            Control parent = control.Parent;
            while (parent != null) {
                if (parent.GetType().FullName.Equals("System.Web.UI.UpdatePanel")) {
                    System.Reflection.PropertyInfo propInfo =
                        parent.GetType().GetProperty("IsInPartialRendering");
                    if (propInfo != null) 
                        return (bool)propInfo.GetValue(parent, null);
                    else
                        return false;
                }
                parent = parent.Parent;
            }
            return false;
        }

        /// <summary>
        /// Determines whether the current postback is being executed in
        /// partial-rendering mode.
        /// </summary>
        /// <param name="page">The page object.</param>
        /// <returns>
        /// <c>true</c> if  the current postback is being executed in
        /// partial-rendering mode; otherwise, <c>false</c>.
        /// </returns>
        public static bool IsInAsyncPostBack(Page page) {
            object scriptManager = FindScriptManager(page);
            if (scriptManager != null) {
                System.Type smClass = GetScriptManagerType(scriptManager);
                System.Reflection.PropertyInfo propInfo = smClass.GetProperty(
                    "IsInAsyncPostBack");
                if (propInfo != null)
                    return (bool)propInfo.GetValue(scriptManager, null);
                else
                    return false;
            }
            return false;

        }

        private static bool ObjectIsInheritedFrom(Object obj, string fullTypeName) {
            Type type = obj.GetType();
            while (type != null) {
                if (type.FullName.Equals(fullTypeName)) return true;
                type = type.BaseType;
            }
            return false;
        }

        private static object FindScriptManager(Control parent) {
            foreach (Control control in parent.Controls) {
                if (ObjectIsInheritedFrom(control, "System.Web.UI.ScriptManager"))
                    return control;
                object result = FindScriptManager(control);
                if (result != null) return result;
            }
            return null;
        }

        private static System.Reflection.Assembly ajaxAssembly = null;

        private static void LoadAjaxAssembly() {
            if (ajaxAssembly == null) {
                ajaxAssembly = System.Reflection.Assembly.LoadFrom(
                    "System.Web.Extensions.dll");
            }
        }

        private static System.Type GetTypeFromAjaxAssembly(string className) {
            LoadAjaxAssembly();
            if (ajaxAssembly != null)
                return ajaxAssembly.GetType(className);
            else
                return null;
        }


        private static Type GetScriptManagerType(Object obj) {
            Type type = obj.GetType();
            while (type != null) {
                if (type.FullName.Equals("System.Web.UI.ScriptManager")) return type;
                type = type.BaseType;
            }
            return null;
        }

        /// <summary>
        /// Registers a client script block which will be rendered on each
        /// asynchronous postback.
        /// Works only if ScriptManager control is existed on the page. Otherwise
        /// does nothing.
        /// </summary>
        /// <param name="page">The Page object that is registering the client
        /// script block.</param>
        /// <param name="type">The type of the client script block. </param>
        /// <param name="key">The string that uniquely identifies the
        /// script block.</param>
        /// <param name="script">A string that contains the script.</param>
        /// <param name="addScriptTags">
        ///  A Boolean value that indicates whether to enclose the script
        /// block in <script> tags.
        /// </param>
        public static void RegisterClientScriptBlock(Page page, Type type,
            string key, string script, bool addScriptTags) {
            object scriptManager = FindScriptManager(page);
            if (scriptManager != null) {
                System.Type smClass = GetScriptManagerType(scriptManager);
                if (smClass != null) {
                    Object[] args = new Object[] { page, type, key, script,
                        addScriptTags };
                    smClass.InvokeMember("RegisterClientScriptBlock", 
                            System.Reflection.BindingFlags.Static |
                            System.Reflection.BindingFlags.Public | 
                            System.Reflection.BindingFlags.InvokeMethod,
                            null, null, args);
                }
            }
        }

        /// <summary>
        /// Registers a script file which to be rendered each time an asynchronous
        /// postback occurs.
        /// Works only if ScriptManager control is existed on the page. Otherwise
        /// does nothing.
        /// </summary>
        /// <param name="page">The Page object that is registering the client
        /// script file.</param>
        /// <param name="type">The type of the client script file.</param>
        /// <param name="key">The string that uniquely identifies the script file.</param>
        /// <param name="url">The URL that points to the script file.</param>
        public static void RegisterClientScriptInclude(Page page, Type type,
            string key, string url) {
            object scriptManager = FindScriptManager(page);
            if (scriptManager != null) {
                System.Type smClass = GetScriptManagerType(scriptManager);
                if (smClass != null) {
                    Object[] args = new Object[] { page, type, key, url };
                    smClass.InvokeMember("RegisterClientScriptInclude", 
                            System.Reflection.BindingFlags.Static |
                            System.Reflection.BindingFlags.Public | 
                            System.Reflection.BindingFlags.InvokeMethod,
                            null, null, args);
                }
            }
        }

        /// <summary>
        /// Registers a script file which to be rendered each time an asynchronous
        /// postback occurs.
        /// Works only if ScriptManager control is existed on the page. Otherwise
        /// does nothing.
        /// </summary>
        /// <param name="page">The page.</param>
        /// <param name="type">The type of the client script file.</param>
        /// <param name="resourceName">The name of resource that contains the
        /// script file.</param>
        public static void RegisterClientScriptResource(Page page, Type type,
            string resourceName) {
            object scriptManager = FindScriptManager(page);
            if (scriptManager != null) {
                System.Type smClass = GetScriptManagerType(scriptManager);
                if (smClass != null) {
                    Object[] args = new Object[] { page, type, resourceName };
                    smClass.InvokeMember("RegisterClientScriptResource",
                            System.Reflection.BindingFlags.Static |
                            System.Reflection.BindingFlags.Public |
                            System.Reflection.BindingFlags.InvokeMethod,
                            null, null, args);
                }
            }
        }
    }
}

现在,使用此类,我们可以修改代码中第一章的 OnPreRender 方法

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

    string scriptCode = "<script type=\"text/javascript\">\n<!--\n";
     . . . . . . . . 
    //script code definition
     . . . . . . . . 
    scriptCode += "// -->\n</script>";

    //register our client script for usual post-back
    //(will do nothing in case of partial udpate)
    Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "MyScriptName",
        scriptCode);

    //register our script during asynchronous postback
    //this code will just do nothing if there is no ASP.NET AJAX installed
    if (Ajax.IsControlInUpdatePanel(this) && Ajax.IsInAsyncPostBack(Page))
        Ajax.RegisterClientScriptBlock(Page, this.GetType(), "MyScriptName",
        scriptCode, false)
}
© . All rights reserved.