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






4.77/5 (14投票s)
在 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 日 - 命名空间已更正