Статьи

ASP.NET и AJAX

В этом руководстве мы рассмотрим некоторые вещи, которые вы можете делать с ASP.NET и AJAX в своих веб-приложениях. Это больше, чем просто оборачивание панели UpdatePanel вокруг некоторых кнопок, текстовых полей и сеток!


Есть много предостережений с произвольным добавлением UpdatePanels на веб-формы и надеждой на лучшее.

Хотя в этом руководстве основное внимание будет уделено другим компонентам, помимо UpdatePanel, может быть полезно взглянуть и на триггеры обратной передачи. Обертывание некоторых элементов управления в веб-форме в UpdatePanel — это дешевый и удобный способ реализации Ajax.

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

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


Элемент управления UpdatePanel указывает, какие области страницы могут обновляться асинхронно.

Давайте начнем с рассмотрения простого элемента управления UpdatePanel и некоторых вещей, которые он может делать из коробки. Элемент управления указывает, какие области страницы могут обновляться асинхронно, и, таким образом, не требуется полная обратная передача страницы.

Создайте новый проект веб-приложения ASP.NET. На страницу default.aspx добавьте элемент управления ScriptManager , элемент управления TextBox с именем txtOutsideUpdatePanel и элемент UpdatePanel. Добавьте ContentTemplate в UpdatePanel и внутри него добавьте элемент управления Button с именем btnInsideUpdatePanel и элемент управления TextBox с именем txtInsideUpdatePanel . Ниже приведены характерные линии из исходного представления:

01
02
03
04
05
06
07
08
09
10
11
<div>
    <asp:ScriptManager ID=»ScriptManager1″ runat=»server»>
    </asp:ScriptManager>
    <asp:TextBox ID=»txtOutsideUpdatePanel» runat=»server»></asp:TextBox>
    <asp:UpdatePanel ID=»UpdatePanel1″ runat=»server»>
        <ContentTemplate>
            <asp:Button runat=»server» Text=»Update» ID=»btnInsideUpdatePanel» />
            <asp:TextBox runat=»server» ID=»txtInsideUpdatePanel»></asp:TextBox>
        </ContentTemplate>
    </asp:UpdatePanel>
</div>

Затем добавьте следующий код в код для этой страницы (или в тег сценария, если вы не используете этот код):

Просмотрите страницу в своем веб-браузере, и вы должны увидеть два текстовых поля в веб-форме. В первом текстовом поле должна быть указана дата и время. Если вы обновите страницу, первое текстовое поле должно обновить дату и время. Нажмите кнопку, и только второе текстовое поле должно обновить дату и время. Таким образом, кнопка вызывает асинхронный почтовый ящик, потому что он находится внутри UpdatePanel.

То, что мы сделали до сих пор, — это простой способ Ajax’изации веб-формы. Мы могли бы легко поместить всю сетку с поддержкой подкачки в UpdatePanel для подкачки без мерцания.

Давайте посмотрим на все это немного подробнее.


Мы можем контролировать, когда элемент управления UpdatePanel отправляет сообщения обратно на основе событий, которые происходят с элементами управления как внутри, так и снаружи самой панели. Вот окно свойств:

В настоящее время нам интересны три свойства:

  • UpdateMode : всегда (по умолчанию) или условно
  • ChildrenAsTriggers : True (по умолчанию) или False
  • Триггеры : набор элементов управления — обсуждается ниже

Существует три допустимых комбинации UpdateMode и ChildrenAsTriggers :

  • Always = True UpdatePanel обновится, когда обновится вся страница, или когда дочерний элемент управления отправит ответ.
  • Всегда = Неверно (неверно)
  • Conditional = True UpdatePanel будет обновляться при обновлении всей страницы, или когда дочерний элемент управления отправляет назад, или триггер из-за пределов UpdatePanel вызывает обновление.
  • Conditional = False UpdatePanel будет обновляться при обновлении всей страницы, или триггер вне UpdatePanel вызывает обновление. Дочерний элемент управления не вызовет обновления.

Следующее свойство, которое нас интересует, это свойство Triggers , которое может быть двух видов:

  • AsyncPostBackTrigger : вызывает асинхронное обновление UpdatePanel
  • PostBackTrigger : вызывает обратную передачу страницы дочерним элементом управления UpdatePanel.

Давайте посмотрим, как они влияют на функциональность UpdatePanel. Вставьте следующий код в веб-форму, а затем код VB.Net под этим в код позади.

У нас есть две кнопки внутри панели и две кнопки снаружи. Мы подключили триггеры так, что кнопка внутри будет вызывать полный постбэк, а кнопка будет вызывать асинхронное обновление.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
<form id=»form1″ runat=»server»>
<asp:ScriptManager ID=»ScriptManager1″ runat=»server» />
<asp:UpdatePanel ID=»UpdatePanel1″ runat=»server»>
    <Triggers>
        <asp:AsyncPostBackTrigger ControlID=»btnAsyncTrigger» />
        <asp:PostBackTrigger ControlID=»btnPostBackTrigger» />
    </Triggers>
    <ContentTemplate>
        <asp:Label ID=»lblInnerTime» runat=»server»></asp:Label>
        <br />
        <asp:Button ID=»btnInnerTime» runat=»server» Text=»Inner Time» />
        <asp:Button ID=»btnPostBackTrigger» runat=»server» Text=»PostBack Trigger» />
    </ContentTemplate>
</asp:UpdatePanel>
<br />
<br />
<asp:Label ID=»lblOuterTime» runat=»server»></asp:Label>
<br />
<asp:Button ID=»btnOuterTime» runat=»server» Text=»Outer Time» />
<asp:Button ID=»btnAsyncTrigger» runat=»server» Text=»Async Trigger» />
</form>

Код-за:

Веб-форма должна выглядеть следующим образом:

Нажатие кнопки Inner Time вызовет асинхронную обратную передачу. Это ожидается, так как кнопка находится внутри панели обновления. Нажатие кнопки « Outer Time приведет к полной обратной передаче страницы. Опять же, это ожидается, так как кнопка Outer Time находится за пределами панели.

Два интересных случая — это PostBack и Async Trigger , на которые мы PostBack в разделе триггеров UpdatePanel. При определении триггеров нам нужно указать ControlID элемента управления, выступающего в качестве триггера, и, необязательно, событие, для которого должен сработать триггер. Если мы пропустим событие, оно сработает в событии по умолчанию для этого элемента управления.

Установив для свойства UpdateMode элемента UpdatePanel значение Conditional , а для ChildrenAsTriggers значение False мы можем контролировать, когда будут выполняться обновления. Асинхронная обратная передача все еще будет выполняться, но мы можем решить, когда отправлять обновленное HTML-содержимое для этой области страницы в браузер.

Вставьте следующий код на страницу ASPX:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<form id=»form1″ runat=»server»>
<div>
    <asp:ScriptManager ID=»ScriptManager1″ runat=»server»>
    </asp:ScriptManager>
    <asp:UpdatePanel ID=»UpdatePanel1″ runat=»server» UpdateMode=»Conditional» ChildrenAsTriggers=»False» >
        <ContentTemplate>
            <asp:Label ID=»lblDateTime» runat=»server» Text=»»></asp:Label><br />
            <asp:Button ID=»btnAsyncPostBack1″ runat=»server» Text=»Inside UpdatePanel 1″ />
            <asp:Button ID=»btnAsyncPostBack2″ runat=»server» Text=»Inside UpdatePanel 2″ />
        </ContentTemplate>
    </asp:UpdatePanel>
    <br />
    <asp:Button ID=»btnSyncPostBack» runat=»server» Text=»Outside UpdatePanel» />
</div>
</form>

И следующий код в его код позади:

Вы должны получить форму, которая выглядит следующим образом:

  • Нажатие на кнопку Inside UpdatePanel 1 вызовет асинхронную обратную передачу, но UpdatePanel не будет обновлен.
  • Нажатие на кнопку Inside UpdatePanel 2 вызовет асинхронную обратную передачу, и мы явно обновляем панель.
  • Нажатие на Outside UpdatePanel приведет к нормальной обратной передаче всей страницы.

Мы можем периодически вызывать обратные вызовы, используя элемент управления таймера ASP.NET. Это полезно для любых регионов веб-формы, для которых нужны текущие / текущие данные, такие как новостные ленты или номера акций в реальном времени. Событие Timer.Tick с интервалом, определенным свойством Interval , которое составляет миллисекунды. Это событие Tick которое мы можем использовать, чтобы вызвать асинхронный или полный постбэк.

То, как управление timer влияет на панель, можно контролировать с помощью коллекции Triggers .

  • Как дочерний элемент управления UpdatePanel, без определенных триггеров: асинхронно обновляется на Timer.Tick
  • Снаружи, без заданных триггеров: Timer.Tick страница Timer.Tick обратно на Timer.Tick
  • В качестве дочернего PostBackTrigger управления с определенным PostBackTrigger : PostBackTrigger страница Timer.Tick обратно на Timer.Tick
  • Снаружи с определенным AsyncPostBackTrigger: UpdatePanel асинхронно обновляется на Timer.Tick

Когда вы добавляете элемент управления ScriptManager в веб-форму, он делает файлы JavaScript клиентской библиотеки ASP.NET доступными для браузера пользователя.

Файлы JavaScript взяты из сборки System.Web.Extensions . Intellisense Visual Studio также будет использовать функциональность, предоставляемую клиентской библиотекой во время разработки.

Добавьте ScriptManager в веб-форму, добавьте новый <script> t , введите Sys. и вы должны увидеть целый ряд новых кусочков для игры. Мы рассмотрим некоторые пространства имен, представленные ниже.

Примеры состоят в основном из кода JavaScript, который принадлежит тегу <script> .

  • Глобальное пространство имен
  • Sys
  • Sys.Net
  • Sys.Serialization
  • Sys.Services
  • Sys.UI
  • Sys.WebForms

Клиентская библиотека предоставляет нам некоторые расширения для существующих объектов JavaScript. Расширения должны сделать работу с объектами JavaScript более похожей на работу с управляемым кодом. Мы также можем очень легко расширить существующие объекты JavaScript самостоятельно. В дополнение к расширению функциональности объектов JavaScript, клиентская библиотека также автоматически подключает ряд событий, которые мы можем очень легко подключить.

Здесь мы используем методы расширения sort() и join() :

1
2
3
var unsortedArray = [5, 4, 3, 2, 1];
var sortedArray = unsortedArray.sort();
alert(sortedArray.join(‘,’));

Здесь мы расширяем объект Array , добавляя метод min() :

01
02
03
04
05
06
07
08
09
10
11
12
13
function minElement() {
    var minimum = Infinity;
    for (var i = 0; i < this.length; i++) {
        if (this[i] < minimum) {
            minimum = this[i];
        }
    }
    return minimum;
}
 
var myArray = [1, 2, 3, 4, 5, 6, 7, 8, 9];
Array.prototype.min = minElement;
alert(myArray.min());

И здесь мы добавляем элементы в массив:

1
2
3
4
5
var myArray1 = [1, 2, 3];
var myArray2 = [5, 6, 7];
 
Array.add(myArray1, 4);
Array.addRange(myArray2, [8, 9]);

Мы можем использовать метод Sys.Debug.trace() для отображения сообщений в отладчике. Это полезно, если вы хотите избежать использования вызовов alert() всех ваших страницах. Сообщения отладчика отображаются в окне «Вывод» в Visual Studio во время сеанса отладки. Таким образом, это означает, что вам нужно «запустить» веб-проект и зайти на страницу или присоединиться к существующему процессу w3p.

В приведенном ниже фрагменте кода мы имеем простой цикл, который вызывает деление на ноль, что может вызвать проблемы в последующих вычислениях. Используя trace() , мы можем распечатать текущее значение переменной счетчика во время выполнения цикла:

1
2
3
4
5
6
7
var counter = 10;
while (counter >= 0) {
    counter -= 1;
    Sys.Debug.trace(«Current value of counter = » + counter);
    var someCalculatedValue = 10 / counter;
    document.write(someCalculatedValue + » «);
}

Теперь давайте используем его, чтобы помочь нам спроектировать и протестировать новый объект JavaScript:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
Employee = function(employeeId, name) {
    this.EmployeeId = employeeId;
    this.Name = name;
}
Employee.prototype = {
    toString: function () {
        return this.EmployeeId + » : » + this.Name;
    },
    get_Name: function () {
        return this.Name;
    },
    set_Name: function (newName) {
        this.Name = newName;
    }
}
Employee.registerClass(«Employee»);
 
var jamie = new Employee(123, «Jamie Plenderleith»);
Sys.Debug.trace(«Before name change : » + jamie.Name);
 
jamie.Name = «Jamie Plenderleith Esq.»;
Sys.Debug.trace(«After name change : » + jamie.Name);

Клиентская библиотека подключает некоторые события страницы, которые мы можем легко подключить. События для конкретной страницы:

  • pageLoad
  • pageUnLoad

И тогда мы можем получить доступ к некоторым событиям, связанным с объектом PageRequestManager которые связаны с асинхронными PageRequestManager :

  • InitializeRequest
  • BeginRequest
  • PageLoading
  • PageLoaded
  • EndRequest

Давайте используем trace() чтобы увидеть, когда сработают эти события:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<form id=»form1″ runat=»server»>
<div>
    <asp:ScriptManager ID=»ScriptManager1″ runat=»server»>
    </asp:ScriptManager>
    <asp:UpdatePanel ID=»UpdatePanel1″ runat=»server»>
        <ContentTemplate>
            <asp:Button ID=»Button1″ runat=»server» Text=»Button» />
        </ContentTemplate>
    </asp:UpdatePanel>
    <script language=»javascript» type=»text/javascript»>
        var myPageRequestManager = Sys.WebForms.PageRequestManager.getInstance();
        myPageRequestManager.add_initializeRequest(onInitializeRequest);
        myPageRequestManager.add_beginRequest(onBeginRequest);
        myPageRequestManager.add_pageLoading(onPageLoading);
        myPageRequestManager.add_pageLoaded(onPageLoaded);
        myPageRequestManager.add_endRequest(onEndRequest);
 
        function pageLoad(sender, args) {
            Sys.Debug.trace(«pageLoad()»);
        }
 
        function onInitializeRequest(sender, args) {
            Sys.Debug.trace(«PageRequestManager.InitializeRequest»);
        }
        function onBeginRequest(sender, args) {
            Sys.Debug.trace(«PageRequestManager.BeginRequest»);
        }
        function onPageLoading(sender, args) {
            Sys.Debug.trace(«PageRequestManager.PageLoading»);
        }
        function onPageLoaded(sender, args) {
            Sys.Debug.trace(«PageRequestManager.PageLoaded»);
        }
        function onEndRequest(sender, args) {
            Sys.Debug.trace(«PageRequestManager.EndRequest»);
        }
 
        function pageUnload(sender, args) {
            Sys.Debug.trace(«pageUnload()»);
        }
    </script>
</div>
</form>

Мы можем даже отменить асинхронную обратную передачу, если хотим:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<form id=»form1″ runat=»server»>
<div>
    <asp:ScriptManager ID=»ScriptManager1″ runat=»server»>
    </asp:ScriptManager>
    <asp:UpdatePanel ID=»UpdatePanel1″ runat=»server»>
        <ContentTemplate>
            <asp:Button ID=»btnUpdateDateTime» runat=»server» Text=»Update» />
            <asp:Label ID=»lblDateTime» runat=»server» Text=»»></asp:Label>
        </ContentTemplate>
    </asp:UpdatePanel>
    <script language=»javascript» type=»text/javascript»>
        var myPageRequestManager = Sys.WebForms.PageRequestManager.getInstance();
        myPageRequestManager.add_initializeRequest(onInitializeRequest);
 
        function onInitializeRequest(sender, args) {
            var someCondition = false;
 
            if (!someCondition) {
                Sys.Debug.trace(«someCondition=false. Aborting postback»);
                args.set_cancel(true);
            }
        }
    </script>
</div>
</form>

Если у пользователя особенно большой ViewState , это приведет к дополнительным издержкам как для них, так и для веб-сервера. Страница удаленного aspx будет проходить почти полный жизненный цикл от загрузки до выгрузки.

Теперь мы рассмотрим вызовы определенных удаленных методов. Они существуют совершенно отдельно от UpdatePanelcontrol, но они, вероятно, будут использоваться совместно для отображения результата вызова некоторого метода.

Когда в элементе управления происходит асинхронная обратная ViewState , на веб-сервер отправляется полная обратная ViewState страницы. Поэтому, если у пользователя особенно большой ViewState , это приведет к дополнительным накладным расходам как для них, так и для веб-сервера. В дополнение к ViewState удаленная страница aspx будет проходить почти полный жизненный цикл от загрузки до выгрузки. Мы можем взаимодействовать с .NET 2.0 ASP.NET Web Services, .Net 4.0 WCF Services (которые в любом случае работают как .Net 2.0 ASP.NET Web Services при использовании HTTP Transport) и с ASP.NET WebForms PageMethods. Мы рассмотрим использование PageMethods.

Метод Page — это общедоступный (статический в C #) метод, определенный в веб-форме, украшенной System.Web.Services.WebMethod() . В дополнение к надлежащему украшению ваших методов, ваш ScriptManager должен иметь для свойства EnablePageMethods значение True . Оттуда вы должны увидеть метод, доступный через прокси-класс PageMethods в JavaScript.

Вот очень простой пример вызова управляемого кода для выполнения вычислений. Вставьте следующее в исходное представление веб-формы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<form id=»form1″ runat=»server»>
<div>
    <asp:ScriptManager ID=»ScriptManager1″ runat=»server» EnablePageMethods=»true»>
    </asp:ScriptManager>
    y = log <sub>b</sub>(x)<br />
    Base b =
    <input id=»txtBase» type=»text» /><br />
    Value x =
    <input id=»txtValue» type=»text» /><br />
    Result y = <span id=»lblResult»>
    <br />
    <input id=»btnCalculate» type=»button» value=»Calculate» />
    <script language=»javascript» type=»text/javascript»>
        $addHandler($get(‘btnCalculate’), «click», btnCalculate_onclick);
 
        function btnCalculate_onclick() {
            PageMethods.CalculateLog($get(‘txtBase’).value, $get(‘txtValue’).value, calculateLog_Finished, calculateLog_Failed);
        }
 
        function calculateLog_Finished(result) {
            $get(‘lblResult’).innerText = result;
        }
 
        function calculateLog_Failed(errObj) {
            Sys.Debug.trace(errObj.get_message());
        }
    </script>
</div>
</form>

И к файлу с выделенным кодом добавьте следующий код:

Вы можете видеть, что это очень простой вызов Math.Log() , но он выполняется асинхронно, не требуя обратной передачи и без использования UpdatePanel. Обратите внимание на PageMethods.CalculateLog() метода PageMethods.CalculateLog() . Он принимает два параметра, требуемых методом в коде позади. В приведенном выше примере следующим параметром является обратный вызов, который необходимо выполнить после успешного завершения вызова метода, а последним параметром является обратный вызов, который необходимо выполнить при возникновении ошибки.

Однако, предостережение о PageMethods : если класс не отображается для вас в JavaScript, вы можете попробовать несколько вещей:

  • Убедитесь, что ваш метод code-behind является Public Shared
  • Убедитесь, что нет ошибок синтаксиса JavaScript
  • Убедитесь, что свойство EnablePageMethods ScriptManager = True
  • Удалите и снова добавьте ScriptManager
  • Выполнить восстановление

Более полный синтаксис для вызова метода PageMethod следующий:

1
PageMethods.MethodName(Param1, Param2 … , ParamN, CompletionCallback, FailureCallback, ContextParameter)

Рассмотрим нашу оригинальную сигнатуру метода CalculateLog_Finished calculateLog_Finished() :

1
function calculateLog_Finished(result) {}

Этот метод может принимать дополнительные параметры:

1
function calculateLog_Finished(result, context, method) {}
  • Result : результат вызова метода
  • Context : значение ContextParameter, первоначально переданное прокси-сервером PageMethods,
    если есть
  • Method : метод, который был вызван — полезно при совместном использовании обратных вызовов

Существует множество аспектов, связанных с Ajax, которые мы не затронули. Тем не менее, в ваших проектах могут быть интересны следующие:

Спасибо за прочтение!