исходные коды
Не рассматривая update panel и не используя закрытые методы класса PageRequestManager из библиотеки ajax, я выделяю для себя способы:
Не рассматривая update panel и не используя закрытые методы класса PageRequestManager из библиотеки ajax, я выделяю для себя способы:
- Page methods
Тут все достаточно просто; достаточно реализовать в web-форме статический серверный метод страницы, пометить его атрибутом WebMethodAttribute, разместить в разметке ScriptManager control, установить у него свойство EnablePageMethods и требуемый метод будет доступен через сгенерированный на стороне клиента объект PageMethods;
В прилагаемом примере в странице PageMethods.aspx я реализовал следующий метод:
[WebMethod(true)]
public static string PageMethodTest(string arg)
{
return "this is a string from server";
}
На стороне клиента разместил ScriptManager следующим образом:
<asp:scriptmanager runat="server" id="scriptManager" enablepagemethods="true">
</asp:scriptmanager>
И зарегестрировал серверную кнопку:
<asp:button id="btnPageMethodInvocation"
runat="server"
text="Page method invocation"
onclientclick="btnPageMethodInvocation_clientHandler();return false;">
</asp:button>
При клике по кнопке будет выполнен клиентский сценарий btnPageMethodInvocation_clientHandler, после чего будет возврат значения false, что говорит о необходимости разовать цепочку вызовов клиентских сценариев, одним из которыхъ является возврат формы (собственно сам postback), а именно его мы и хотим избежать; Таким образом, при клике на форму у нас должен выполниться только сценарий btnPageMethodInvocation_clientHandler;
Сам сценарий будет следующим:
function btnPageMethodInvocation_clientHandler() {
PageMethods.PageMethodTest("test", onSuccessfullHandler);
}
Как видите, происходит обращение к глобальному клиенскому объекту PageMethods, и вызывается метод, который мы описали на серверной стороне; Сигнатура данного клиентского метода будет отличаться только наличием дополнительных параметров, которые указывают на javascript функции успешного и неуспешного вызова серверного метода, для того чтобы собственно говоря и реализовать сам принцип асинхронности – сделав вызов PageMethods.PageMethodTest, выполнение сценария не прерывается, а результат будет передан в onSuccessfullHandler функцию с единственным параметром result в случае успешного выполнение callback, как показано ниже:
function onSuccessfullHandler(result) {
alert(result);
}
Сигнатура самого метода следующая:Сигнатура метода
Здесь я просто показываю значение параметра result, значение которого мы получаем со стороны сервера;
Иными словами, все выполнение происходит следующим образом:
ScriptManager, проанализировав codebehind класс страницы и обнаружив статик метод с атрибутом WebMethod, сгенерировал объект PageMethods и соотвествующий по сигнатуре клиенский метод, которые, собственно и реализует асинхронный вызов одноименного серверного метода, передав значение со стороны клиента; Серверный метод делает возврат значения (в нашем примере мы возвращаем простой тип данных, о том как делать возврат сложных типов данных с применением json сериализации/десериализации я расскажу в следующем посте), которое в процессе выполнения callback будет передано в клиентский сценарий, который мы указали onSuccessfullHandler;
Подводя итог, цепочка вызовов будет следующей:
function btnPageMethodInvocation_clientHandler()
PageMethods.PageMethodTest("test", onSuccessfullHandler)
public static string PageMethodTest(string arg)
function onSuccessfullHandler(result)
Результат вызова нашей страницы будет слеюущим:Результат вызова
Данный вариант реализации асинхронного вызова очень удобен на мой взгляд, если бы не огромный минус – а именно, невозможность его использования в User-контролах; - Script Service
Второй вариант лишен описанного выше минуса; Смысл заключается в следующем – реализуется web-сервис, code behind класс сервиса помечается атрибутом ScriptService, а методы, которые мы хотим использовать на стороне клиента – атрибутом ScriptMethod; В разметке самой страницы обязательно присуствие ScriptManager с указанием того ScriptService, методы которого мы хотим получить и использовать на стороне клиента;
В нашем случае я сделал следующий web-service:
namespace Callbacks.ScriptServices
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ScriptService()]
public class ScriptService : System.Web.Services.WebService
{
[WebMethod]
[ScriptMethod]
public string ScriptServiceTest(string arg)
{
return "this is a string from script service method";
}
}
Создал страницу ScriptServiceCallPage.aspx, код разметки которой выглядит следующим образом:
<title></title>
<script language="javascript" type="text/javascript"
function btnPageMethodInvocation_clientHandler() {
Callbacks.ScriptServices.ScriptService.ScriptServiceTest("test",
onSuccessfullHandler);
}
function onSuccessfullHandler(result) {
alert(result);
}
</script>
<form id="form1" runat="server">
<asp:scriptmanager runat="server" id="scriptManager" enablepagemethods="true">
<services>
<asp:servicereference path="~/ScriptServices/ScriptService.asmx">
</asp:servicereference>
</services>
<div>
<asp:button id="btnPageMethodInvocation"
runat="server" text="Script service method invocation"
onclientclick="btnPageMethodInvocation_clientHandler();
return false;">
</asp:button></div>
</asp:scriptmanager>
</form>
Отличие от предыдущего примера с использованием сгенерированного объекта PageMethods в том, что мы указываем конкретный ScriptService в ScriptManager, который генерирует соотвествующий javascript класс с методами, сигнатура который соотвествует серверным методам + добавляет параметры для указания javascript функций в случае успешного или неудачного выполнения асинхронного вызова; Сгенерированный объект, который мы можем использовать на стороне клиента, будет иметь имя, полностью совпадающего с указанным script service, т.е. включая c# namespace и имя класса – в нашем случае это Callbacks.ScriptServices.ScriptService; В остальном принцип работы с script service ничем не отличается от PageMethods; Достоинство данного способа состоит в том, что часто используемые операции, вызов которых требуется на клиенте, сосредоточены в одном месте, и доступны как из классов форм, так и user controls; - Третий способ, который лично мне больше всего нравится и на мой взгляд наиболее гибок и прозрачнее для меня, является явная реализация интерфейса IcallbackEventHandler user контролом или классом страницы; Данный способ также описан у Дино Эспозито, автора множества книг-бестселлеров по технологиям AJAX и ASP.NET;
В нашем примере я создал страницу ICallbackEventHandlerImplementor.aspx, у code behind класса реализовал интерфейс ICallbackEventHandler как показано ниже:
public partial class ICallbackEventHandlerImplementor : System.Web.UI.Page, ICallbackEventHandler
{
#region ICallbackEventHandler Members
public string GetCallbackResult()
{
string resultFromServer = "this is a string from ICallbackEventHandler implementor";
return resultFromServer;
}
public void RaiseCallbackEvent(string eventArgument)
{
string[] argsFromClient = eventArgument.Split(',');
if (argsFromClient.Length >= 2)
{
switch (argsFromClient[0])
{
case "add":
break;
default:
break;
}
}
}
#endregion
}
Метод RaiseCallbackEvent будет вызываться со стороны клиентаи принимаеть едиственный строковый параметр, значение которого мы сформируем на стороне клиента; Метод GetCallbackResult будет использоваться для формирования результата, который мы вернем обратно клиентской стороне;
Итак, что же будет являться началом асинхронной операции – а началом асинхронной операции со стороны клиента будет являться вызов функции WebForm_DoCallback, сигнатура да и собственно название которого не стоит запоминать и использовать напрямую, так они могуто измениться в следующих версиях; Вместо этого сам код для вызова мы можем получить, используя класс ClientScriptManager через свойство ClientScript текущей страницы, используй метод GetCallbackEventReference; Данный метод генерирует вызов метода WebForm_DoCallback на стороне клиента, который мы можем использовать в нашем клиентском сценарии; В нашем примере я хочу сгенерировать клиенский сценарий serverCall, который бы принимал единственное строковое значение, отправлял бы его на выполнение серверу и собственно на этом бы его функции бы заканчивались:
Т.е. что-то типа такого:
function serverCall(arg)
{
WebForm_DoCallback(…arg)
}
Как это сделать, показано в реализации обработчика Page_Load нашей рассматриваемой страницы:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
string webFormDoCallbackScript = this.ClientScript.GetCallbackEventReference(this, "arg", "onSuccessfullHandler", null, true);
string serverCallScript = "function serverCall(arg){" + webFormDoCallbackScript + ";\n}\n";
if (!this.ClientScript.IsClientScriptBlockRegistered("serverCallScript"))
{
this.ClientScript.RegisterClientScriptBlock(this.GetType(), "serverCallScript", serverCallScript, true);
}
}
}
Что здесь происходит? Сначала я получаю в виде строки сам вызов WebForm_DoCallback, причем указываю, как будет называться его единственный аргумент, а также имя той javascript функции, в которую будет передан результат со стороны сервера по завершении асинхронного вызова; затем я формирую функцию враппер наз функцией асинхронного вызова – а именно нашу функцию serverCall – после запуска страницы в сгенерированном исходном тексте вы увидите следующий скрипт:
<script type="text/javascript">
//<![CDATA[
function serverCall(arg){WebForm_DoCallback('__Page',arg,onSuccessfullHandler,null,null,true);
}
//]]>
</script>
Теперь же можно использовать нашу функцию-враппер serverCall со стороны клиента, как показано в разметке страницы:
<head id="Head1" runat="server">
<title></title>
<script language="javascript" type="text/javascript">
function btnPageMethodInvocation_clientHandler() {
serverCall('add,' + 10);
}
function onSuccessfullHandler(result) {
alert(result);
}
</script>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:Button ID="btnPageMethodInvocation"
runat="server"
Text="ICallbackEventHandlerImplementor sample"
OnClientClick="btnPageMethodInvocation_clientHandler();return false;" />
</div>
</form>
</body>
</html>
Сама разметка, как видите, практически не отличается от предыдущих страниц, за исключением того, что здесь не требуется ScriptManager; Ограничение в количестве параметров, передаваемых в функцию асинхронного вызова WebForm_DoCallback, не ограничивает однако нас от формирования такой строки, значение которой мы можем рассматривать как несколько параметров, что собственно и продемонстрировано в примере:
На клиенте мы передаем serverCall('add,' + 10); строку “Add, 10”, на стороне сервера делаем сплит строки и дальше уже анализируем полученные значения для выполнения какого либо действия на стороне сервера незаметно от клиента;
Лично мне последний способ нравится больше в виду того, что я получаю больший контроль над самим асинхронным вызовом, чем в первых двух описанных;
ps: Существуют и другие способы асинхронных вызовов, например,в том же самом DevExpress есть даже специальный серверный контрол, в котором указывается javascript handler на получение результата со стороны сервера и серверный обработчик, который и выполняет анализ данных со стороны клиента;
6 комментариев:
Мне тоже последний метод понравился больше всего - не надо пложить веб-сервисы и можно использовать не только на страницах, но и в контролах.
Но, честно говоря, немного отпугнуло ограничение на передачу только одного параметра - мне кажется парсить строку как-то не очень наглядно........
одна строка - это нормально, с тех пор как существует json. сериализуй туда чо душе угодно
Можете подсказать почему у меня 3 вариант не работает в DotNetNuke? Почему то не срабатывает функция которая возвращает значение на сервер!
Можете подсказать почему у меня 3 вариант не работает в DotNetNuke? Почему то не срабатывает функция которая возвращает значение на сервер!
У меня второй метод не рабоает. Бьюсь...Ошибка "Callbacks не определен"...
Можете прислать кусочек вашего кода на guid.empty@gmail.com или прямо сюда определение сервиса и объявление aspx ascx?
Отправить комментарий