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

创建一个抽象的 AJAX 用户控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.73/5 (10投票s)

2007年7月27日

CPOL

7分钟阅读

viewsIcon

47040

downloadIcon

363

本文介绍了创建抽象类所需的步骤,该类已处理了派生控件的 AJAX 请求。

引言

在开发使用 AJAX 功能的 ASP.NET 网站时,迟早您会意识到开发基于 AJAX 的 Web 控件的过程开始变得重复:您最终会编写相同的 JavaScript 来创建和处理客户端以及服务器端的 XmlHttpRequest。此时,您可能已经有一个外部 JavaScript 文件,该文件已在您的母版页中引用,**然后**您会发现所有内容都开始演变成一个巨大的 JavaScript 怪物。好吧,至少,这就是我所经历的。当我最终不得不面对一个控件需要在页面上实例化几次的问题时,我停下来思考一个更优雅的解决方案。如果我有一个抽象的 AJAXControl 类,我的控件可以从中派生,并且只添加该控件显式使用的 JavaScript(和服务器端处理)呢?

背景

要完全理解本文,您需要了解 AJAX 的工作原理。我不会详细介绍,我将向您展示如何创建一个基础类,该类可以以最小的努力实现 AJAX 开发。您将看到单个控件如何同时渲染其内容**和**处理客户端 JavaScript 请求,如何将 JavaScript 嵌入自定义控件 DLL,以及如何注册它。

使用代码

只需解压源代码。有主项目 AbstractAjaxControl,其中包含抽象控件以及一个派生自我们的抽象基类的演示控件(因此可以实例化),还有一个使用所述演示控件的简单网站。

捕获 AJAX 请求

我只打算介绍使用 HTTP_POST 发送的 AJAX 请求。AJAX POST 请求由两条 JavaScript 语句触发,然后再实际发送数据

httpRequest.open('POST', uri, true);

其中 'POST' 指定我们将使用 HTTP_POST,uri 是我们正在发布到的文档的地址,最后一个参数将请求设置为阻塞(false,同步)或非阻塞(true,异步)。

httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

这告诉 JavaScript 发送请求,就像表单被提交一样。通过单击“提交”按钮发送表单与通过这种方式发送 XmlHttpRequest 之间没有区别,除了一个,这就是我们所使用的:ASP.NET 确实识别 Request.Form 属性,但它**不**识别回发事件。这在 ASP.NET 驱动的网站上是一种非常罕见的条件,这就是为什么我们将使用它的原因。

我们希望我们的抽象基类能够处理所述条件;我们希望派生类仅实现如何响应 AJAX 请求。为此,我声明了一个 private 事件 AjaxRequest

private delegate void AjaxRequestEventHandler(object sender, AjaxRequestEventArgs e);

private event AjaxRequestEventHandler AjaxRequest;
private void OnAjaxRequest()
{
    if (AjaxRequest != null)
        AjaxRequest(this, new AjaxRequestEventArgs(Page.Request.Form));
}

AjaxRequestEventArgs 是一个 EventArgs 类,它在其公共属性 Parameters 中存储 Request.Form NameValueCollection 中的所有表单元素。

问题是:何时应触发事件?

  1. 没有回发事件
  2. 我们的 Request.Form NameValueCollection 中有内容
  3. 这些内容是相关的,例如,在控件的范围内
  4. 在页面的其余部分初始化(或呈现)之前

请参阅 c) 点,我们需要一个变量来设置范围,并且我们需要所有发送数据的 JavaScript 来提供此数据的范围。我是在服务器端完成的,通过声明一个 private 成员 _scope,并具有一个公开可用的属性 Scope(该属性可以并且应该由派生类声明性地设置)。如果页面上存在一个以上派生自我们的抽象类的控件,Scope 将非常有用。

由于 d) 条件,找到触发 AjaxRequest 的正确时机很容易。我们需要重写 OnInit 方法。

if (!Page.IsPostBack && Page.Request.Form["scope"] == _scope)
    OnAjaxRequest();
base.OnInit(e);

就 AJAX 请求的服务器端处理而言,就这样。不过,不要忘记订阅该事件,并告知所有派生类它们绝对必须实现一个方法来处理 AJAX 请求

public AjaxControl()
{
    AjaxRequest += new AjaxRequestEventHandler(HandleAjaxRequest);
}

protected abstract void HandleAjaxRequest();

我们需要多少 JavaScript?

我们已经完成了一半。记住,我们不希望任何需要引用母版页的庞大 JavaScript 文件?我们想要一个所有派生自我们抽象基类的控件都使用的基本 JavaScript,再加上定义派生控件行为的少量 JavaScript 片段,并且仅随这些控件一起部署。想一想:您能想到所有 AJAX 控件有什么共同之处?哪些 JavaScript 函数属于基本功能?

  1. 声明一个(全局)XmlHttpRequest 对象
  2. 一个用于创建此类对象的函数
  3. 一个用于准备给定数据并发送它们的功能

所有 AJAX JavaScript 的不同之处在于响应的处理,因此这不是我们能放在基本 JavaScript 文件中的。剩下的就是这个

var httpRequest = null;

function CreateRequest()
{
    if (window.XMLHttpRequest)
        httpRequest = new XMLHttpRequest();
    else if (window.ActiveXObject)
        httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
}

function SendRequest(parameters, responseHandler)
{
    if(httpRequest == null)
        CreateRequest();
        
    httpRequest.open('POST', document.location.href, true);
    httpRequest.setRequestHeader('Content-Type', 
                'application/x-www-form-urlencoded');
    httpRequest.onreadystatechange = responseHandler;
    
    strPostData = "";
    for(i = 0; i < parameters.length; i++)
    {
        strPostData += parameters[i] + "=";
        i++;
        strPostData += parameters[i];
        if(i + 1 != parameters.length)
            strPostData += "&";
    }
    httpRequest.send(strPostData);
} 

我的全局 XmlHttpRequest 对象是 httpRequest,它在(首次需要时)被创建一次。在 CreateRequest 方法中,我首先检查 XmlHttpRequest 是否已知(IE 7、Opera、Mozilla 为 true),如果失败则回退到旧的 ActiveXObjectSendRequest 接受一个键值对形式的参数数组(即:"key1", "value1", "key2", "value2"...等等),将参数准备为要发送的字符串,并使用 HTTP_POST 异步发送到调用它的页面(记住:我们的控件处理自己的 AJAX 请求——这就是为什么请求被发送到的 URI 是包含我们控件的页面的 URI)。

嵌入您的脚本

我最头疼的部分是如何将脚本嵌入我的 DLL 并从那里使用它。事实证明,如果您知道如何做,这并不难。这需要四个步骤

  1. 为您的项目创建一个资源文件(右键单击项目,属性 -> 资源)
  2. 将一个 .js 文件添加到您的资源中
  3. 将您的 .js 文件的“生成”属性设置为“嵌入式资源”:右键单击 js 文件(通常放在名为“resources”的子文件夹中),然后您就完成了
  4. assemby:webresource 属性设置为
  5. [assembly: WebResource("ProjectName.Resources.Filename.js", "text/javascript")]

    此属性直接插入到基类文件中的命名空间声明之上。

尽量避免在 JavaScript 文件名中使用任何点。相信我,这可以为您节省几年寿命。

嵌入脚本后,您可以将其注册到控件所在的 ASP 页面。使用属性中使用的确切名称进行此操作;例如,我在重写的 OnInit 方法中注册基本的 AJAX JavaScript,在调用 base.OnInit 之后立即进行。

Page.ClientScript.RegisterClientScriptResource(typeof(AjaxControl), 
                      "AbstractAjaxControl.Resources.AjaxBase.js");

您可以在控件代码中的几乎任何点注册它。

演示控件

由于如果您尝试实例化一个抽象类(毕竟,这就是它抽象的原因)可能会感到失望,因此我包含了一个派生自 AjaxControl 的类来向您展示如何做到这一点。这相当直接

  1. 添加一个新类
  2. 将其派生自 AjaxControl
  3. 像编写普通的 ASP.NET 控件一样实现控件(也就是说,负责 RenderContents() 方法,或者如果您更像我,则负责 CreateChildControls() 方法)。
  4. 实现继承的 HandleAjaxRequest() 方法(并确保它调用 Response.End(),我们不希望页面发生任何进一步的处理)。
  5. 编写一个 JavaScript 方法来处理 XmlHttpResponse,以及一个用于准备和调用 SendRequest() 方法的方法。
  6. 如果您愿意,可以将 JavaScript 嵌入到您的 DLL 中。不过,您不必这样做。

就是这样。

关注点

的确,这种方法比使用 ASP.NET AJAX 扩展要费力得多。另一方面,它的开销要小得多,并且您可以完全控制您的页面做什么和不做什么。而且在没有安装 ASP.NET AJAX 的环境中,安装选项也不可行。这就是最初促使我手动完成所有 AJAX 工作的原因。

如果可能,请尝试使用嵌入式资源中的 JavaScript。不再维护不断增长的 JavaScript 文件夹确实是值得的。

历史

  1. 第一个版本,无历史记录:2007/07/27。
© . All rights reserved.