воскресенье, января 17, 2010

CallbackHandlerControl - серверный контрол для выполнения callback-запроса своими руками :)


исходные коды

По мотивам темы Как выполнить callback со стороны клиента на сервер в ASP.NET

Итак, чтобы каждый раз не писать/дублировать код - создаем контрол, обязанностями которого будет выполнение Callback-запроса на сервер и возврат ответа со стороны сервера и возврат его на клиента ...

При создании такого запроса был использован именно третий подход, который и описан в статье выше...
При размещении контрола на странице нам необходимо будет указать имя-клиентского сценария для обработки результата в случае успешного завершения callback, имя клиентского сценария для обработки ситуации в случае неуспешного завершения, и самое важное - имя сгенерированной javascript функции, которую мы будем использовать для выполнения самого запроса на сервер со стороны клиентского javascript.
Последняя функция будет принимать в качестве единственного параметра строку, которую мы сможем обработать на серверной стороне.

Итак, все сказанное может выглядеть в разметке страницы или вашего user-control следующим образом:



<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="CallbackHandlerControl._Default" %>

<%@ Register assembly="CallbackHandlerControl" namespace="CallbackHandlerControl.Controls" tagprefix="cc1" %>

<!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>This is a Callback Handle Control Test Page</title>

<script language="javascript" type="text/javascript">
function btnCallServer_clientHandler() {
callServerHandler("argument");
}

function onSuccessfullHandler(result) {
alert(result);
}

function onErrorClientHandler(result) {
}
</script>

</head>
<body>
<form id="form1" runat="server">
<div>

<h1>This is a Callback Handle Control Test Page</h1><br />
<asp:button ID="btnCallServer"
runat="server" text="Script service method invocation"
OnClientClick="btnCallServer_clientHandler();return false;">
</asp:button>

<cc1:CallbackEventHandler ID="callbackHandler" runat="server"
OnClientDataReceived="callbackHandler_ClientDataReceived"
OnSuccessfullClientHandler="onSuccessfullHandler"
OnErrorClientHandler="onErrorClientHandler"
ServerCallFunctionName="callServerHandler"/>

</div>
</form>
</body>
</html>





Обращаем внимание на серверный обработчик серверного события ClientDataReceived - OnClientDataReceived -
данный обработчик будет использован на стороне сервера при получении данных со стороны клиента,
а также для формирования данных, которые будут отправлены на клиента в качестве ответа.



Сам code-behind класс страницы:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace CallbackHandlerControl
{
/// <summary>
/// This is a Callback Handle Control Test Page
/// </summary>
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{

}

#region event handlers
protected void callbackHandler_ClientDataReceived(object sender, CallbackHandlerControl.Controls.ClientDataReceivedEventArgs e)
{
string clientData = e.ClientData;
e.Cancel = false;
e.ServerResponse = DateTime.Now.ToString();
}
#endregion
}
}




Итак, сам текст нашего контрола:


using System;
using System.Collections.Generic;
using System.Linq;
using System.ComponentModel;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace CallbackHandlerControl.Controls
{

/// <summary>
/// Кконтрол выполняет callback обращение на серверную сторону со стороны клиента
/// </summary>
[ToolboxData("<{0}:CallbackEventHandler ID=\"callbackHandler\" runat=\"server\"> </{0}:CallbackEventHandler>")]
public partial class CallbackEventHandler : System.Web.UI.WebControls.WebControl, ICallbackEventHandler
{
#region events
/// <summary>
/// Данное событие будет подниматься в случае получения данных со стороны клиента
/// на сервеной стороне в контроле <see cref="WebSite.WebModules.Common.Callbacks.CallbackEventHandler"/>
/// </summary>
[Browsable(true)]
[Category("Callback Handlers")]
[Description("Данное событие будет подниматься в случае получения данных со стороны клиента на сервеной стороне в контроле WebSite.WebModules.Common.Callbacks.CallbackEventHandler")]
public event EventHandler<ClientDataReceivedEventArgs> ClientDataReceived;

/// <summary>
/// Данные, полученные со стороны клиента, а также отправляемые со стороны сервера клиенту
/// </summary>
private ClientDataReceivedEventArgs Arguments = null;
#endregion

#region fields & properties

/// <summary>
/// ViewState ключи
/// </summary>
private string SuccessfullClientHandlerViewStateID
{
get
{
return this.ID + "_SuccessfullClientHandler";
}
}

/// <summary>
/// ViewState ключи
/// </summary>
private string ErrorClientHandlerViewStateID
{
get
{
return this.ID + "_ErrorClientHandler";
}
}

/// <summary>
/// ViewState ключи
/// </summary>
private string ServerCallFunctionNameViewStateID
{
get
{
return this.ID + "_ServerCallFunctionName";
}
}

/// <summary>
/// Имя сценария, который будет вызван в случае успешного обращения на сервер
/// </summary>
[Browsable(true)]
[Category("Callback Handlers")]
[DefaultValue("onSuccessfullClientHandler")]
[Description("Имя сценария, который будет вызван в случае успешного обращения на сервер")]
public string OnSuccessfullClientHandler
{
get
{
if (ViewState[SuccessfullClientHandlerViewStateID] != null)
return (String)ViewState[SuccessfullClientHandlerViewStateID];
else
return "";
}
set
{
ViewState[SuccessfullClientHandlerViewStateID] = value;
}
}

/// <summary>
/// Имя сценария, который будет вызван в случае неудачного обращения на сервер
/// </summary>
[Browsable(true)]
[Category("Callback Handlers")]
[DefaultValue("onErrorClientHandler")]
[Description("Имя сценария, который будет вызван в случае неудачного обращения на сервер")]
public string OnErrorClientHandler
{
get
{
if (ViewState[ErrorClientHandlerViewStateID] != null)
return (String)ViewState[ErrorClientHandlerViewStateID];
else
return "";
}
set
{
ViewState[ErrorClientHandlerViewStateID] = value;
}
}

/// <summary>
/// Имя функции, которая будет использоваться для вызова серверного сценария со стороны клиента, принимает один аргумент ARG в виде строки
/// </summary>
[Browsable(true)]
[Category("Callback Handlers")]
[DefaultValue("serverCall")]
[Description("Имя функции, которая будет использоваться для вызова серверного сценария со стороны клиента, принимает один аргумент ARG в виде строки ")]
public string ServerCallFunctionName
{
get
{
if (ViewState[ServerCallFunctionNameViewStateID] != null)
return (String)ViewState[ServerCallFunctionNameViewStateID];
else
return "";
}
set
{
ViewState[ServerCallFunctionNameViewStateID] = value;
}
}
#endregion

protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
string webFormDoCallbackScript = this.Page.ClientScript.GetCallbackEventReference(this, "arg", this.OnSuccessfullClientHandler, null, true);
string serverCallScript = "function " + this.ServerCallFunctionName + "(arg){" + webFormDoCallbackScript + ";\n}\n";

if (!this.Page.ClientScript.IsClientScriptBlockRegistered(this.ServerCallFunctionName))
{
this.Page.ClientScript.RegisterClientScriptBlock(this.GetType(), this.ServerCallFunctionName, serverCallScript, true);
}
}

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

}

#region ICallbackEventHandler Members

public string GetCallbackResult()
{
// прошла обработка данных в Argumets на стороне сервера, отправляем результат на клиента
if (this.Arguments != null)
{
return this.Arguments.ServerResponse;
}
else
return "";
}

public void RaiseCallbackEvent(string eventArgument)
{
this.OnClientArgumentReceived(eventArgument);
}

#endregion

#region event handlers

public virtual void OnClientArgumentReceived(string eventArgument)
{
if (this.ClientDataReceived != null)
{
this.Arguments = new ClientDataReceivedEventArgs
{
ClientData = eventArgument
};

this.ClientDataReceived(this, this.Arguments);
}

}
#endregion

}
}


Обратите внимание на реализацию интерфейса ICallbackEventHandler, смысл которого был описан ранее в статье Как выполнить callback со стороны клиента на сервер в ASP.NET


Обращаем также внимание на код обработчика callbackHandler_ClientDataReceived в коде нашего code-behind класса страницы,
который был декларативно указан в разметке страницы в обработке контрола в строке
OnClientDataReceived="callbackHandler_ClientDataReceived".


protected void callbackHandler_ClientDataReceived(object sender, CallbackHandlerControl.Controls.ClientDataReceivedEventArgs e)
{
string clientData = e.ClientData;
e.Cancel = false;
e.ServerResponse = DateTime.Now.ToString();
}


Именно в нем мы получаем данные со стороны клиента, и подготавлиаем данные, отправляемые на клиента со стороны сервера



Данный обработчик принимает аргумент класса-наследника EventArgs - ClientDataReceivedEventArgs, которое содержит как данные, полученные со стороны клиента, так и данные, которые
будут отправлены клиентской стороне в качестве ответа от сервера. Нужно помнить также о том, что мы всегда можем отправить как со стороны сервера, так и со стороны клиента сложные объекты, использую сериализацию в строку -
потому вполне достаточно ограничиться строковоыми типами данных.





Само событие в коде нашего контрола выглядит следущим образом:

/// <summary>
/// Данное событие будет подниматься в случае получения данных со стороны клиента
/// на сервеной стороне в контроле <see cref="WebSite.WebModules.Common.Callbacks.CallbackEventHandler"/>
/// </summary>
[Browsable(true)]
[Category("Callback Handlers")]
[Description("Данное событие будет подниматься в случае получения данных со стороны клиента на сервеной стороне в контроле WebSite.WebModules.Common.Callbacks.CallbackEventHandler")]
public event EventHandler<ClientDataReceivedEventArgs> ClientDataReceived;




Как видите, обработчик события ClientDataReceived должен принимать аргумент класса ClientDataReceivedEventArgs, код которого представлен ниже:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace CallbackHandlerControl.Controls
{
/// <summary>
/// Параметры события ClientDataReceivedEvent, которое будет подниматься в случае получения данных со стороны клиента
/// на сервеной стороне в контроле <see cref="WebSite.WebModules.Common.Callbacks.CallbackEventHandler"/>
/// </summary>
public class ClientDataReceivedEventArgs : EventArgs
{

/// <summary>
/// Строка, отправленная со стороны клиента на сервер - это может быть как простой аргумент, так и
/// серилиазованное состояние объекта
/// </summary>
public string ClientData = "";

/// <summary>
/// Ответ от сервера, отправляемый на клиента - это может быть сериализованное состояние объекта или просто строка;
/// Логика по обработке данной строки лежит на стороне клиента
/// </summary>
public string ServerResponse = "";

/// <summary>
/// Данный флаг определяет, завершить цепочку выполнения Callback или нет
/// </summary>
public bool Cancel = false;
}
}




Ну и пара скриншотов:

результат выполнения callback по клику на кнопке на странице, разметка которого приведена в начале страницы:






Контрол в режиме редактирования на странице





Удачи в использовании.

ps: возможно, получилось сумбурно, но мне кажется, разобравшись с принципом проведения Callback-запроса третьим описанным способом из статьи Как выполнить callback со стороны клиента на сервер в ASP.NET
код (доступен по ссылке в начале статьи ) и принцип работы самого контрола становится понятным.

Комментариев нет: