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

在 ASP.NET 中处理多个客户端回调

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (14投票s)

2009年1月7日

CPOL

3分钟阅读

viewsIcon

57811

downloadIcon

603

在 ASP.NET 页面和控件中处理多个客户端回调的一种巧妙方法。

引言

自 ASP.NET 2.0 以来,.NET framework 提供了一个名为客户端回调的功能。这些回调比 ASP.NET AJAX Extensions 的 UpdatePanel 提供的部分回发轻得多。客户端回调将指定的字符串发送到服务器,并仅检索服务器端生成的字符串作为结果,而不是像 UpdatePanel 那样重新呈现部分页面。

Nasir Ali Khan 在 CodeProject 上关于客户端回调的文章中做出了出色的工作。

本文包括以下三个部分

  • 多个客户端回调实现。
  • 简单的 ASP.NET 页面示例。
  • 自定义复合控件示例。

先决条件

本文的读者需要熟悉 .NET framework 2.0 版本中提供的客户端回调概念。

第一部分:多个客户端回调实现

ICallbackEventHandler 接口的实现将页面/控件限制为单个回调方法声明(RaiseCallbackEvent(string eventArgument))。

其他人建议

为了区分 RaiseCallbackEvent 方法中的回调,其他文章建议在 eventArgument 字符串前添加前缀。作者认为这是一种不美观且容易出错的方法。

更聪明的方法

一个自定义控件应该作为一个“壳”,专门用于托管客户端回调功能

using System.Web.UI;

[assembly: TagPrefix("HelveticSolutions.Web.UI.WebControls", "HelveticSolutions")]
namespace HelveticSolutions.Web.UI.WebControls
{
    /// <summary>
    /// This control encapsulates a client callback and redirects it
    /// through an exposed event.
    /// </summary>
    [ToolboxData("<{0}:ClientCallback runat="server"></{0}:ClientCallback>")]
    public class ClientCallback : Control, ICallbackEventHandler
    {
        #region event/delegate declaration
        public delegate string OnRaiseCallbackEvent(string eventArgument);
        public event OnRaiseCallbackEvent Raise;
        #endregion

        #region members
        private string callbackResult = string.Empty;
        #endregion

        ///<summary>
        /// Processes a callback event that targets this control. 
        ///</summary>
        ///<param name="eventArgument">A string that represents an event argument to 
        /// pass to the event handler.</param>
        public void RaiseCallbackEvent(string eventArgument)
        {
            if (Raise != null) callbackResult = Raise(eventArgument);
        }

        ///<summary>
        ///Returns the results of a callback event that targets this control.
        ///</summary>
        ///<returns>The result of the callback.</returns>
        public string GetCallbackResult()
        {
            return callbackResult;
        }
    }
}

该控件实现 ICallbackEventHandler 接口,并将回调方法公开为一个公共事件(Raise)。

通过将尽可能多的 ClientCallback 控件添加到页面或用户/自定义控件,可以很好地将每个回调的实现分开。

第二部分:简单的 ASP.NET 页面示例

在 Visual Studio 设计器中,可以通过拖放轻松地将 ClientCallback 控件放置到 ASP.NET 页面上。以下代码示例包含两个 ClientCallback 控件和两个启动回调的 Button。为了简单起见,两个 Button 使用相同的回调完成函数。但是,在实际应用中,您将为每个 Button 实现一个函数,以便相应地处理结果。

<%@ Page Language="C#" AutoEventWireup="true" 
  CodeBehind="Default.aspx.cs" Inherits="TestWeb._Default" %>
<%@ Register Assembly="HelveticSolutions.Web.UI.WebControls" 
  Namespace="HelveticSolutions.Web.UI.WebControls" TagPrefix="SmartSoft" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>HelveticSolutions - Multiple client callbacks example</title>
    <script type="text/javascript">
        function button1Clicked(arg, context, callback) {
            <%= Button1CallbackReference %>;
        }

        function button2Clicked(arg, context, callback) {
            <%= Button2CallbackReference %>;
        }

        function callbackComplete(result, context) {
            document.getElementById("result").innerHTML = result;
        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <input type="button" onclick="button1Clicked('', '', 
                             callbackComplete);" value="Button 1" />
            <input type="button" onclick="button2Clicked('', '', 
                             callbackComplete);" value="Button 2" />
            <div id="result"></div>
        </div>
        <SmartSoft:ClientCallback ID="Button1Callback" 
                   runat="server"></SmartSoft:ClientCallback>
        <SmartSoft:ClientCallback ID="Button2Callback" 
                   runat="server"></SmartSoft:ClientCallback>
    </form>
</body>
</html>

后台代码

using System;
using System.Web.UI;

namespace TestWeb
{
    public partial class _Default : Page
    {
        #region properties
        protected string Button1CallbackReference 
          { get { return GetCallbackReference(Button1Callback); } }
        protected string Button2CallbackReference 
          { get { return GetCallbackReference(Button2Callback); } }
        #endregion

        #region page life-cycle events
        protected void Page_Init(object sender, EventArgs e)
        {
            // Register client callback events
            Button1Callback.Raise += Button1Callback_Raise;
            Button2Callback.Raise += Button2Callback_Raise;
        }
        #endregion

        #region private methods
        /// <summary>
        /// Handles client callback events for button 1.
        /// </summary>
        /// <param name="eventArgument">The event argument
        ///               of the callback.</param>
        /// <returns>The result of the callback.</returns>
        private string Button1Callback_Raise(string eventArgument)
        {
            return "Button 1 callback processed.";
        }

        /// <summary>
        /// Handles client callback events for button 2.
        /// </summary>
        /// <param name="eventArgument">The event argument
        ///                 of the callback.</param>
        /// <returns>The result of the callback.</returns>
        private string Button2Callback_Raise(string eventArgument)
        {
            return "Button 2 callback processed.";
        }

        private string GetCallbackReference(Control control)
        {
            return Page.ClientScript.GetCallbackEventReference(control, "arg", 
                                     "callback", "context");
        }
        #endregion
    }
}

ClientCallback 控件事件在 Page_Init 方法中被挂钩。重要的是要注意回调事件引用需要分配给相应的 ClientCallback 控件。

此示例中的按钮发送一个空字符串作为参数,并且检索到的结果也只是一个简单的文本。但是,如果您考虑交换 JSON 或 XML,这些客户端回调可能会变得非常强大!

第三部分:自定义复合控件示例

当涉及到自定义 Web 控件开发时,很好地组织客户端回调会变得更加方便。以下复合控件实现了与第二部分中的页面相同的功能。

using System.Reflection;
using System.Web.UI;
using System.Web.UI.WebControls;

[assembly: TagPrefix("HelveticSolutions.Web.UI.WebControls", "HelveticSolutions")]
namespace HelveticSolutions.Web.UI.WebControls
{
    [ToolboxData("<{0}:SampleCompositeControl 
              runat="server"></{0}:SampleCompositeControl>")]
    public class SampleCompositeControl : CompositeControl
    {
        #region members
        private Button button1;
        private Button button2;
        private ClientCallback button1Callback;
        private ClientCallback button2Callback;
        #endregion

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

            // Register client side script for the callbacks
            string clientScript = GetResource("SmartSoft.Web.UI." + 
                   "WebControls.Resources.SampleCompositeControlClientScript.js");
            clientScript = clientScript.Replace("{button1_callback_reference}", 
                                        GetCallbackReference(button1Callback));
            clientScript = clientScript.Replace("{button2_callback_reference}", 
                                        GetCallbackReference(button2Callback));
            Page.ClientScript.RegisterClientScriptBlock(GetType(), 
                              "client_script", clientScript, true);
        }

        protected override void CreateChildControls()
        {
            // Create buttons
            button1 = new Button
                {
                    ID = "Button1",
                    Text = "Button 1",
                    OnClientClick = "button1Clicked('', '', " + 
                                    "callbackComplete);return false;"
                };
            Controls.Add(button1);

            button2 = new Button
                {
                    ID = "Button2",
                    Text = "Button 2",
                    OnClientClick = "button2Clicked('', '', " + 
                                    "callbackComplete);return false;"
                };
            Controls.Add(button2);

            // Create callback controls
            button1Callback = new ClientCallback {ID = "button1Callback"};
            button1Callback.Raise += button1Callback_Raise;
            Controls.Add(button1Callback);

            button2Callback = new ClientCallback {ID = "button2Callback"};
            button2Callback.Raise += button2Callback_Raise;
            Controls.Add(button2Callback);
        }

        protected override void RenderContents(HtmlTextWriter writer)
        {
            // Render buttons
            button1.RenderControl(writer);
            button2.RenderControl(writer);

            // Render result div
            writer.AddAttribute(HtmlTextWriterAttribute.Id, "result");
            writer.RenderBeginTag(HtmlTextWriterTag.Div);
            writer.RenderEndTag();

            // Render callback controls
            button1Callback.RenderControl(writer);
            button2Callback.RenderControl(writer);
        }

        /// <summary>
        /// Handles client callback events for button 1.
        /// </summary>
        /// <param name="eventArgument">The event argument
        ///               of the callback.</param>
        /// <returns>The result of the callback.</returns>
        private string button1Callback_Raise(string eventArgument)
        {
            return "Button 1 callback processed.";
        }

        /// <summary>
        /// Handles client callback events for button 2.
        /// </summary>
        /// <param name="eventArgument">The event argument
        ///             of the callback.</param>
        /// <returns>The result of the callback.</returns>
        private string button2Callback_Raise(string eventArgument)
        {
            return "Button 2 callback processed.";
        }

        /// <summary>
        /// Helper to load embedded resource as a string.
        /// </summary>
        /// <param name="resourceName">Resource name.</param>
        /// <returns>A string that represents the resource content.</returns>
        private static string GetResource(string resourceName)
        {
            Assembly assembly = Assembly.GetExecutingAssembly();
            string result = string.Empty;
            Stream resourceStream = 
              assembly.GetManifestResourceStream(resourceName);

            if (resourceStream != null)
            {
                using (TextReader textReader = 
                       new StreamReader(resourceStream))
                {
                    result = textReader.ReadToEnd();
                }
            }
            return result;
        }

        private string GetCallbackReference(Control control)
        {
            return Page.ClientScript.GetCallbackEventReference(control, "arg", 
                                     "callback", "context");
        }
    }
}

客户端脚本作为资源嵌入在程序集中,并且它包含两个占位符,这两个占位符在 OnPreRender 方法中被实际的回调事件引用替换。

重要的是 ClientCallback 控件被分配一个 ID。否则,ASP.NET 将无法将客户端回调分配给正确的控件。

历史

  • 2009 年 7 月 1 日 - 首次发布。
  • 2014 年 11 月 14 日 - 命名空间已更正
© . All rights reserved.