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

使用 .NET 2.0 内置的 AJAX 支持

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.26/5 (8投票s)

2007年6月22日

CPOL

7分钟阅读

viewsIcon

45328

downloadIcon

229

本文介绍如何仅使用 .NET 2.0 的内置功能来创建一个简单的 AJAX 控件。

引言

我的职业生涯大部分时间都在 Microsoft SharePoint 和 Microsoft Content Management Server 2002 环境中度过,并学会了欣赏自定义 WebControls 的价值——在 SharePoint 中,我们有 WebParts,它们只是华丽的 WebControls;而在 MCMS 2002 中,我们有 Placeholders,它们也是已变异的 WebParts。

今天,当我查看任何应该托管在 SharePoint 或普通 .NET 网页上的自定义功能时,我都会以自定义控件的视角来看待它们。每个自定义控件都可以包含许多其他标准和自定义控件,并且它们都可以根据需要包含子控件。我认为这是大多数人在编写代码时使用的相同的自顶向下方法。我们有方法,或者方法调用其他方法,它们可以调用更多方法等等。

如上所述,我确信您可以推断出本文将讨论使用 .NET 语言之一编写的自定义 Web 控件;然而,我的重点仅在于展示如何构建自定义控件,使其仅使用 .NET 2.0 Framework 提供的标准功能来使用一些 AJAX 功能。

在本文中,Microsoft ASP.NET AJAX _(前身为 Microsoft Atlas)_ 不以任何形式使用。使用本文描述的技术,您可以例如在 SharePoint 2007 Web Parts 中添加 AJAX 功能,而无需部署任何额外的库——方法是实现 `ICallbackEventHandler` 接口。

复选框示例

在本文中,我将创建一个简单的 AJAX 启用复选框,但首先让我解释一下为什么这样的复选框会很有用:假设我们正在创建一个销售视图控件。该控件将列出当天完成的所有销售(可能是一个很大的列表)。在查看列表时,销售经理可以为对他来说重要的项目“加红旗”。之后,他可以应用一个过滤器来查看整个列表或仅查看他简要列出的项目。一个示例界面可能如下所示:

Screenshot - salesData.jpg

现在的问题是如何实现“红旗”功能?使用传统的策略,我们可以做以下两件事之一:

  1. 让复选框控件在被点击时自动回发。
  2. 在列表的底部(和顶部)放置一个标有“更新”的按钮,该按钮在被点击时将存储用户的“红旗”选择。

这两种方法都不是很理想——如果我们让整个页面回发并刷新,销售经理会非常沮丧,因为每次他选择某些内容时,他都会厌倦不断闪烁的页面,并厌倦等待页面加载完成。如果我们有一个保存选择的按钮,销售经理可能会忘记点击它,关闭浏览器,当他稍后返回时,他会发现自己必须再次查看整个列表,重新选择他感兴趣的所有内容,并希望记得点击保存按钮……

一个更友好的界面是用户可以简单地勾选他们感兴趣的项目对应的复选框,而无需页面回发,也无需记住在某处点击保存按钮。为了实现这一点,我们将需要 AJAX 的强大功能。

AJAX 复选框

我将要创建的是一个可重用的 AJAX 复选框控件,它不仅与上面描述的 ACME 销售视图相关,而且还可以作为任何需要 AJAX 复选框的自定义控件的一部分。

声明复选框

我们的控件从以下声明开始:

public class AjaxCheckBox : WebControl, INamingContainer, ICallbackEventHandler

`WebControl` 和 `INamingContainer` 接口只是我们创建自定义控件时始终实现的标准接口,重要的是 `ICallbackEventHandler` 接口,我们将使用它来启用复选框的 AJAX 功能。为了实现这个接口,我们的控件必须有两个方法,即 `RaiseCallbackEvent` 和 `GetCallbackResult`。`RaiseCallbackEvent` 方法是在 AJAX 回调到服务器时执行的方法,而 `GetCallbackResult` 方法由客户端浏览器用来获取来自回调的任何数据的字符串。

自定义 `ICallbackEventHandler` AJAX 控件使用的典型逻辑如下:

  1. 在页面加载时,控件通过 `Render` 方法正常渲染自身。在 `Render` 方法中,一个容器控件(例如 `div` 标签)也将被渲染到客户端,该容器将显示任何 AJAX 结果/动态创建的用户界面。
  2. 用户点击一个按钮,或执行某个操作,该操作将执行一个 JavaScript 函数来启动 AJAX 回调。
  3. 服务器代码调用 `RaiseCallbackEvent` 方法并执行必要的处理(此方法接收一个从 JavaScript 方法传递的字符串参数)。
  4. `RaiseCallbackEvent` 方法将任何需要传递回客户端的结果存储在服务器上的类级别字符串变量中。
  5. 客户端脚本调用 `GetCallbackResult` 方法并从服务器接收字符串结果。
  6. 客户端脚本处理字符串结果,并将结果 HTML 渲染到客户端容器控件(`div`)中。

此处创建的复选框不会执行任何客户端处理,因为在用户单击复选框后,客户端没有更多要执行的操作,但有关上述完整模型的示例,请参阅源代码中提供的 `Validate` 示例。

复选框事件

我已经为复选框实现了一个自定义事件,当复选框的值更改时,它将通知包含该复选框的控件。这实际上是该控件的一个非常整洁的部分——当调用 `RaiseCallbackEvent` 时,您可以从该方法中触发事件,并且事件的侦听器实际上会收到事件通知,即使这是一个 AJAX 回调。下面的代码显示了自定义事件的相关部分(您会注意到这里没有什么花哨的,这是我们从 2003 年就已知的标准代码)。

#region Delegates
/// <summary>
/// The delegate for the check changed event
/// </summary>
/// <param name="value">The value of the check box.</param>

public delegate void CheckChangedDelegate(bool value);
#endregion
 
#region Events
/// <summary>

/// The check changed event.

/// </summary>

public event CheckChangedDelegate CheckChanged;
#endregion
 
#region On Check Changed
/// <summary>

/// Execute the check changed event if it has been wired up.

/// </summary>

/// <param name="value">The value of the text box.</param>

protected void OnCheckChanged(bool value)
{
      if (this.CheckChanged != null)
            this.CheckChanged(value);
}
#endregion

在销售示例中,我们的视图控件将包含几个这样的复选框,并为它们设置事件处理程序。当 AJAX 文本框触发其事件时,销售视图将相应地更新数据库,用户会认为这是一个 AJAX 回调的结果,但他们不知道。

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
 
namespace DrainCleaner.Controls
{
      /// <author />Hannes Foulds</author />
      /// <date>11 June 2007</date>
      /// <summary>
      /// Test the ajax check box with event.
      /// </summary>

      public class CheckTest : WebPart, INamingContainer
      {
            #region User Interface Elements
            /// <summary>The AJAX textbox to test.</summary>

            protected AjaxCheckBox chkTest;
            #endregion
 
            #region Render
            /// <summary>
            /// Render the control.
            /// </summary>
            /// <param name="writer">The writer to use for rendering.</param>

            protected override void Render(HtmlTextWriter writer)
            {
                  this.chkTest.RenderControl(writer);
            }
            #endregion
 
            #region Create Child Controls
            /// <summary>

            /// Create the child controls.

            /// </summary>

            protected override void CreateChildControls()
            {
                  this.chkTest = new AjaxCheckBox();
                  this.chkTest.ID = "chkTest";
                  this.Controls.Add(this.chkTest);
 
                  this.chkTest.CheckChanged += 
                    new AjaxCheckBox.CheckChangedDelegate(chkTest_CheckChanged);
            }
            #endregion
 
            #region Event Handlers
            /// <summary>

            /// The event handler for the check box change.

            /// </summary>

            /// <param name="value">The check box value.</param>

            void chkTest_CheckChanged(bool value)
            {
                  bool test = value;
                  
                  // perform logic here to do a database update

                  // with the value or do whatever you need to

                  // store the boolean

            }
            #endregion
      }
}

执行 AJAX

我做的第一件事是渲染 AJAX 文本框的用户界面,如下所示。您会注意到没有创建服务器端复选框,而是渲染了一个简单的 `` 标签。客户端 `onclick` 事件负责调用适当的 JavaScript 函数来执行 AJAX 回调。

下面的示例中的 `clientValue` 是 AJAX 回调到服务器 `RaiseCallbackEvent` 方法时将执行的客户端 JavaScript,用于获取要传递给服务器的值;而 `callbackRef` 变量包含要为回调执行的客户端 AJAX JavaScript 字符串。

#region Render
/// <summary>
/// Render the interface for the control.
/// </summary>
/// <param name="writer">The writer used for rendering the control.</param>

protected override void Render(HtmlTextWriter writer)
{
      string clientValue = string.Format("document.getElementById('{0}').checked", 
                           this.CheckBoxID);
      string callbackRef = this.Page.ClientScript.GetCallbackEventReference(this, 
                           clientValue, "null", null);
 
      writer.WriteLine("<input id=\"{0}\" type=\"checkbox\" " + 
                       "onclick=\"javascript:{1}\" />", 
                       this.CheckBoxID, callbackRef);
}
#endregion

拼图的最后几块是实现 `ICallbackEventHandler` 接口所需的两个函数的实现。`RaiseCallbackEvent` 方法解析它从客户端接收到的字符串值,然后调用将执行任何已连接事件的方法。`GetCallbackResult` 方法简单地返回 `null`,因为将不会执行任何其他客户端处理。

#region Get Callback Result
/// <summary>
/// Get the result of a client side callback.
/// </summary>
/// <returns>The callback result string.</returns>

public string GetCallbackResult()
{
      return null;
}
#endregion
 
#region Raise Callback Event
/// <summary>

/// Raise the client callback event

/// </summary>

/// <param name="eventArgument">The event arguments.</param>

public void RaiseCallbackEvent(string eventArgument)
{
      bool value = Boolean.Parse(eventArgument);
      this.OnCheckChanged(value);
}
#endregion

完整代码

为了帮助您全面了解,我已将 AJAX 复选框的完整代码放在下面:

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
 
namespace DrainCleaner.Controls
{
      /// <author>Hannes Foulds</author>

      /// <date>11 June 2007</date>

      /// <summary>

      /// This control creates a ajax check box.

      /// </summary>

      public class AjaxCheckBox : WebControl, INamingContainer, ICallbackEventHandler
      {
            #region Delegates
            /// <summary>

            /// The delegate for the check changed event

            /// </summary>

            /// <param name="value">The value of the check box.</param>

            public delegate void CheckChangedDelegate(bool value);
            #endregion
 
            #region Events
            /// <summary>

            /// The check changed event.

            /// </summary>

            public event CheckChangedDelegate CheckChanged;
            #endregionregion
 
            #region Properties
            /// <summary>

            /// The ID of the control that AJAX results should be displayed in.

            /// </summary>

            protected string CheckBoxID
            {
                  get { return string.Concat(this.ClientID, "_result"); }
            }
            #endregion
 
            #region Render
            /// <summary>

            /// Render the interface for the control.

            /// </summary>

            /// <param name="writer">The writer used for rendering the control.</param>

            protected override void Render(HtmlTextWriter writer)
            {
              string clientValue = string.Format("document.getElementById('{0}').checked", 
                                                 this.CheckBoxID);
              string callbackRef = this.Page.ClientScript.GetCallbackEventReference(this, 
                                        clientValue, "null", null);
 
              writer.WriteLine("<input id="\"{0}\"" onclick="\"javascript:{1}\"" />", 
                               this.CheckBoxID, callbackRef);
            }
            #endregion
 
            #region Get Callback Result
            /// <summary>
            /// Get the result of a client side callback.
            /// </summary>
            /// <returns>The callback result string.</returns>
            public string GetCallbackResult()
            {
                  return null;
            }
            #endregion
 
            #region Raise Callback Event
            /// <summary>
            /// Raise the client callback event
            /// </summary>
            /// <param name="eventArgument">The event arguments.</param>
            public void RaiseCallbackEvent(string eventArgument)
            {
                  bool value = Boolean.Parse(eventArgument);
                  this.OnCheckChanged(value);
            }
            #endregion
 
            #region On Check Changed
            /// <summary>
            /// Execute the check changed event if it has been wired up.
            /// </summary>
            /// <param name="value">The value of the text box.</param>
            protected void OnCheckChanged(bool value)
            {
                  if (this.CheckChanged != null)
                        this.CheckChanged(value);
            }
            #endregion
      }
}

结论

我希望本文至少能给您带来一些思考,如果您仍然不相信事件处理程序已被执行,请在 `bool test = value;` 处设置一个断点并自行测试 ;-)

在本篇文章的可下载源代码中,我还包含了一个验证控件,它完成了更复杂一些的任务。我最近实现了一个类似这样的控件,它根据数据库验证资产编号,并且效果相当不错。

AJAX 是一项非常强大的技术,我坚信它将长期存在,因为它无疑极大地提升了用户体验。在未来的文章中,我将探讨 ASP.NET AJAX 及其精彩之处。

© . All rights reserved.