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

Magic AJAX:将 AJAX 应用于现有网页

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.82/5 (66投票s)

2005年9月16日

MIT

12分钟阅读

viewsIcon

1067212

downloadIcon

2700

如何在不替换 ASP.NET 控件和/或编写 JavaScript 代码的情况下,将 AJAX 技术应用到您的网页。

引言

在 AJAX 技术如此盛行和备受关注之后,现在开发者有许多 AJAX 解决方案可供选择。缺点是您必须用自定义的 AJAX 控件替换网页中的标准 ASP.NET 控件,和/或为客户端编写 JavaScript 代码来处理服务器返回的数据。

如果有一个“神奇”的面板控件,当您拖放普通的、老式的 ASP.NET 控件时,“神奇地”将它们转换为支持 AJAX 的控件怎么办?不可能!嗯,这篇文章声称这样的面板确实存在(AjaxPanel),而且遗憾的是,它没有任何魔法;全都是纯粹的 C# 代码。

更新 - 2005 年 11 月 12 日: MagicAjax 框架现已托管在 SourceForge。自最初发布以来,已经增加了许多改进和功能,包括对 ASP.NET 2.0 的支持。

背景

本文假设您知道什么是 AJAX。如果不是,CodeProject 上有许多好文章可以帮助您入门。本文的代码基于 Bill Pierce 出色的系列文章 AJAX WAS HERE。要使用本文中的类,您不需要理解客户端 JavaScript 框架及其如何调用服务器方法,但如果您想了解“幕后”发生了什么,我强烈建议阅读 Bill Pierce 的文章。

使用代码

我包含了一个演示项目,向您展示如何将现有的普通回发控件转换为类似 AJAX 的控件。

BubisChat.aspx

这个页面试图实现一个聊天应用程序。我不会深入探讨它是如何工作的;它仅用于演示目的。它使用标准的 ASP.NET 控件,没什么神秘的。按钮会导致向服务器回发,页面会重新加载并用新数据填充控件。

AjaxBubisChat.aspx

此页面使用相同的控件,但工作方式却大不相同。控件即时刷新,无需浏览器重新加载。没错,AJAX 来了

嘿,你是怎么做到的

将 *BubisChat.aspx* 转换为 *AjaxBubisChat.aspx*(或将 AJAX 应用到任何使用此框架的页面)需要四个步骤。

  • 让页面继承自 AjaxPage(可选)

    public class AjaxBubisChat : Ajax.AjaxPage

    继承自 AjaxPage 不是必需的;它仅处理回调事件并提供一些有用的属性以方便使用。要在继承自 AjaxPage 的页面中处理回调事件,请重写 OnCallBack 方法。

    protected override void OnCallBack(EventArgs e)
    {
        // Refreshes the sessionID in the cache
        PutSessionIDInCache();
    
        txtMsg.Text = chatData.msgText.ToString();
        ShowNames();
    
        base.OnCallBack (e);
    }

    如果页面不继承自 AjaxPage,则可以通过实现 ICallBackEventHandler 接口来处理回调事件。

    public interface ICallBackEventHandler
    {
        void RaiseCallBackEvent();
    }

    回调类似于回发,但无需浏览器重新加载页面。我稍后会解释回调的作用。出于稍后会变得清晰的原因,在回调期间,页面及其控件的 Load 事件不会被引发。您必须改用回调事件

    在回调期间,页面的 HttpContext 是无效的,因此您无法使用 System.Web.UI.PageRequest/Response 属性,而必须使用 CallBackHelper.RequestCallBackHelper.Response 属性。AjaxPage 提供了有效的 Request/Response 属性,因此您无需在代码中将它们替换为 CallBackHelper 中的属性。

  • 将需要无回发刷新的控件放入 AjaxPanel 中。

    每个 TextBoxListBox 可以有一个 AjaxPanel,或者所有这些控件可以有一个 AjaxPanel。按钮应放在 AjaxPanel 中,以便它们的提交功能自动替换为回调函数。在此示例中,为了方便起见,我只将所有控件放在一个 AjaxPanel 中。

  • 配置 web.config 文件

    <httpModules>
        <add name="AjaxHttpModule" type="Ajax.AjaxHttpModule, Ajax" />
    </httpModules>

    放在 <system.web> 部分,并将

    <!-- If CallBackScriptPath is not set in the appSettings, 
                                     "/ajax/script" is used-->
    <add key="CallBackScriptPath" value="/ajax/script" />

    放在 web.config 文件的 <appSettings> 部分。

    AjaxHttpModuleHttpApplicationAcquireRequestState 事件中处理回调,该事件在请求经过身份验证之后。如果您将源文件提取到 wwwroot 路径,则默认脚本路径是有效的。如果您将其提取到其他目录,则应相应地更改应用程序设置中的 CallBackScriptPath

  • 启用页面上的 CallBackTimer(可选)

    // For automatic CallBack every 3 seconds.
    CallBackHelper.SetCallBackTimerInterval(3000);

    如果聊天文本框需要自动刷新以显示新消息,则需要此设置。大多数页面不需要自动刷新,因此如果不是这种情况,请忽略此项。

信不信由你,就这么简单!无需 JavaScript,也无需替换控件。您可以通过代码或使用 Visual Studio Designer 将新控件添加到 AjaxPanel

那么,回调做了什么?

回调的作用是调用服务器端控件事件(以及一个特殊的 CallBackTimer 事件,如果已启用)。有关更多详细信息,我建议阅读我在背景部分提到的 Bill Pierce 的文章。当服务器收到回调时,它会返回通用的 JavaScript 代码。客户端并不关心 JavaScript 做什么(填充 ListBox、调用警报框,等等),它只是执行它们。因此,与通常的 AJAX 应用程序的心态相反,由服务器负责使用 JavaScript 操作页面,而不是客户端。这样,与其试图将 JavaScript 代码嵌入网页并将其与服务器代码同步,不如将重点放在服务器端实现自定义控件的所有功能,而无需将代码分离到 JavaScript 部分和 C#(VB.NET,等等)部分。

现在,事情变得更有趣了……支持 AJAX 的页面被存储为会话状态变量。当调用回调时,AjaxHttpModule 会拦截它,从会话中找到原始页面,并调用页面控件的相应事件,而无需重新加载原始页面。

为什么将页面存储在会话中?

  • 由于页面及其控件不断重新加载,因此没有开销。
  • AJAX 的错觉是页面表现得像桌面应用程序。在桌面应用程序中,控件会一直保留在内存中,而无需每次按下按钮时都重新加载它们,并且可以动态地添加或删除控件。嗯,通过将页面持久化到会话中,我们不必重新加载控件,并且可以动态地添加或删除控件;如果您将它们添加到 AjaxPanel,它们实际上也会出现在浏览器中!

为了使页面能够存储在会话中,它必须至少包含一个 AjaxControl 控件(AjaxPanel 的基类)。使用的会话密钥是原始页面的 URL,以便区分不同的页面。

回调不一定来自 AjaxPanel 包含的控件,它可以从页面上的任何控件调用,只要该控件已正确配置为调用相应的回调函数,如下所示:

Button btnSend = new Button();
btnSend.Attributes.Add ("onclick", 
   CallBackHelper.GetCallbackEventReference(btnSend) + 
   " return false;");

CallBackHelper.GetCallbackEventReference 方法在客户端提供 AJAXCbo.DoPostCallBack 调用,并添加了 `return false;` 以便 OnClick 事件可以覆盖提交函数。

默认情况下,AjaxPanel 会自动配置其包含的所有提交按钮以调用回调函数,并将普通回发的 __doPostBack 调用替换为 AJAXCbo.DoPostCallBack 调用。如果您想手动设置控件的 OnClick 事件,请将 AjaxPanelSetCallBackForSubmitButtonsSetCallBackForChildPostBack 属性设置为 false

神秘的 AjaxPanel

AjaxPanel 的任务是在每次调用回调时将内容反映到客户端浏览器。为了实现这一点,它会扫描其包含的控件,并为每个添加、删除或修改的控件生成相应的 JavaScript 代码,而忽略未发生更改的控件。为了检测更改,它会渲染每个控件并检查生成的 HTML 是否与上一次回调期间获得的 HTML 不同。

此外,如果 AjaxPanel 遇到任何 RenderedByScriptControl 控件(AjaxPanel 继承自此类),它会忽略它们,并让它们负责将自己“反映”到浏览器。因此,如果一个 AjaxPanel(父级)包含另一个 AjaxPanel(子级),并且子 AjaxPanel 的控件发生更改,父 AjaxPanel 不会发送子 AjaxPanel 的整个 HTML 渲染,而是子 AjaxPanel 只发送更改控件的 HTML。这样,客户端响应回调的 JavaScript 代码的大小就会大大减小。

限制

已知限制包括:

  • 您无法删除 AjaxPanel 控件的属性。删除不会反映在页面上。请使用
    ajaxPanel.Attributes["attrib"] = "";

    实例。

  • 默认情况下,AjaxPanel 中的控件不会调用客户端页面验证。您需要“手动”在控件的 OnClick 事件中插入适当的 JavaScript。
  • 仅在 Internet Explorer 和 FireFox 中进行测试。如果浏览器不支持 XmlHttp,则没有替代方案。
  • 会话必须是“InProc”模式。“SQLServer”和“StateServer”模式不受支持。

总结

我将不深入研究类的内部工作细节。我已尝试记录所有方法,因此任何想扩展提供的 AJAX 控件的人,我强烈建议阅读所有方法和类的注释。对于那些只想使用框架的人

  • 演示页面的脚本路径仅在您将源文件提取到 wwwroot 路径时才有效。如果您将其提取到其他目录,则应相应地更改 web.config 的应用程序设置中的 CallBackScriptPath
  • 请遵循我在本文使用代码部分提到的四个步骤。
  • 阅读 AjaxPage.csCallBackHelper.csICallBackEventHandler.cs 的注释。
  • 阅读 AjaxPanelpublic 属性的注释。
  • 阅读 AjaxLinkButton 控件的注释。
  • 您可以使用 CallBackHelper.Write 将自定义 JavaScript 发送到客户端。
  • 在您的页面代码中,您必须使用 CallBackHelper.RequestCallBackHelper.Response 属性,除非它继承自 AjaxPage。同样适用于您的用户控件和提供的 AjaxUserControl
  • 不要在 AjaxPanel 中使用 Panel 控件,因为即使 Panel 的一个子项发生更改,所有子项都必须在客户端渲染。请改用 AjaxPanel 控件。
  • 在 2005 年 9 月 23 日的更新后,该框架可以在回调期间处理 Response.RedirectServer.Transfer 方法。您无需在代码中更改它们。
  • 在回调期间,请使用 CallBackHelper.End 而不是 Response.End
  • 始终牢记,回调与回发不同,因为页面及其控件会持久化在会话中,因此不会引发 Load 事件,并且您可以动态地向 AjaxPanel 添加/删除控件,更改将反映在浏览器中。有点像操作桌面应用程序的控件。

关注点

AjaxPanel 还可以处理“浏览器后退按钮”问题。“浏览器后退按钮”问题是指,按下后退按钮时,浏览器会从缓存中加载 HTML 页面,因此对页面所做的任何 AJAX 更改都会丢失,而用户仍然期望看到他们之前查看过的页面。

为了解决这个问题,我在页面中放置了一个每次页面加载时都会执行的 JavaScript 函数和一个空的隐藏字段。该函数会检查此隐藏字段,如果为空,它会假设页面是通过请求服务器加载的(即通过刷新按钮),然后设置字段的值。如果函数发现字段不为空,它会假设浏览器是通过后退按钮加载页面的(字段的值会被恢复),并调用服务器上的一个特殊回调(CallBackStartup)。当引发 CallBackStartup 事件时,AjaxPanel 会将所有子项渲染到客户端页面,从而恢复用户之前查看的页面。

结论

我希望您觉得这个框架很有用。我鼓励您尝试使用它,如果您从中创造出一些奇特、令人惊叹、令人眼花缭乱、令人瞠目结舌的控件,请与我们分享。

历史

  • 2005 年 9 月 16 日 - 初始发布。
  • 2005 年 9 月 16 日 - 在文章中添加了浏览器检测限制,由 Cristian O. 指出。
  • 18-9-2005
    • 现在无需继承自 AjaxPage 即可使用 AJAX Framework。将其功能移至其他类,除了回调事件的处理和 Request/Response 属性。
    • 添加了 AjaxUserControl.cs,其功能与 AjaxPage 类似。
    • AjaxHttpModule 替换了 AjaxHttpHandler
    • 添加了 IPostDataLoadedEventHandler.cs
    • 添加了 NoVerifyRenderingPage.cs
    • CallBackHelper.cs 中添加了一些辅助函数和属性。
    • 其他小的添加和改进。
    • 更新了文章的文本。
    • 修复了 AjaxPanelSetCallBackForChildPostBack 中的一个错误,由 Cristian O. 指出。
  • 21-9-2005
    • 修复了 CallBackObject.js 以使复选框正常工作(由 collab man 指出)。
    • 添加了 CallBackHelper.Redirect 方法,并将其包含在文章的总结部分(由 mdissel 指出)。
  • 23-9-2005
    • AjaxHttpModule 现在可以在回调期间处理 Response.RedirectServer.Transfer。不需要 CallBackHelper.Redirect。更新了文章的总结部分。
  • 27-9-2005
    • 修复了一个由 collab man 指出的错误(当由 CheckBoxListBox 调用回调时,控件未刷新)。
    • 修复了一个由 JunkyMail1 指出的错误(多选 ListBox 工作不正常)。
    • Cristian O. 提供了对 CallBackObject.js 的增强(“加载中...”指示器、回调超时、错误期间返回的响应显示在页面上)。
  • 30-9-2005
    • AjaxPanel 现在在其 Visible 属性更改时能正常工作。
    • 修复了由 mdissel 指出的隐藏控件持久化状态的错误。
  • 6-10-2005
    • 修复了由 CarinLindberg 指出的 RadioButtonList 问题。
  • 8-10-2005
    • CallBackObject.js 中,修复了当 POST 数据包含 '+' 时无法正确发送的问题,由 Ricardo Stuven 指出。
  • 11-10-2005
    • 包含了 Ricardo Stuven 提供的处理 CheckBoxList 的修复。
  • 12-11-2005
    • 在文章中包含了 MagicAjax 的 SourceForge 和主页链接。
© . All rights reserved.