澳门新葡萄京官网注册 15

澳门新葡萄京官网注册慎用 ASP.NET 的 UpdatePanel

ASP.NET 的 UpdatePanel 使我们几乎不需要什么 Ajax 知识就可以开发出 Ajax
效果的网页,这得归功于 UpdatePanel。

2. 条件更新

       如果页面上有多个 UpdatePanel 控件且都是完全独立的,那么可以配置
UpdatePanel.UpdateMode 属性由 Always 改为 Conditional
以独立更新它们
。即,如果将先前示例中的 3 个 UpdatePanel 控件都设置为
UpdateMode=”Conditional” ,那么它们将各自独立工作。

       从技术面而言,单击某个按钮时本例中所有的 Label
都会被更新,但是客户端只有部分页面会被刷新以显示相应的效果。多数情况下,这种差别并不重要。但是,它可能会导致异常,因为每个
Label 的值都被保存到了视图状态。这样,下一次页面被送到服务器端时,所有
Label 都将被置为最近的值。

 

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm2.aspx.cs" Inherits="WebApplication1.WebForm2" %>    <!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>注册</title>      <script type="text/javascript">          function check() {              var Name = document.getElementById("txtName").value;              if (Name == "") {                  alert("昵称不能为空!");              }              else {                  WebApplication1.WebService1.CheckName(Name, callbackCheck);                  function callbackCheck(result) {                      if (result == false) {                          document.getElementById("checkFont").innerHTML = "该昵称已存在!请重新输入!";                      }                      else {                          document.getElementById("checkFont").innerHTML = "恭喜!该昵称可以使用!";                      }                  }              }            }      </script>  </head>  <body>      <form id="form1" runat="server">      <asp:ScriptManager runat="server" ID="scriptManager">          <Services>              <asp:ServiceReference Path="~/WebService1.asmx" />          </Services>      </asp:ScriptManager>      <asp:UpdatePanel ID="Upnl" runat="server" UpdateMode="Always">          <ContentTemplate>              <div>                  昵称:<asp:TextBox ID="txtName" runat="server"></asp:TextBox><div id="checkFont">                  </div>              </div>          </ContentTemplate>          <Triggers>              <asp:AsyncPostBackTrigger ControlID="btnCheck" />              <asp:PostBackTrigger ControlID="btnReFresh" />          </Triggers>      </asp:UpdatePanel>      <asp:Button ID="btnCheck" runat="server" OnClientClick="check();" Text="检测昵称" />      <asp:Button ID="btnReFresh" runat="server" Text="刷新页面" />      </form>  </body>  </html>  

我曾经做了一个系统,把“增、删、改、查”都做在一个页面中。按照 Ajax
的理念:我提交一个添加,只返回添加成功与否。如果添加成功,则用
JavaScript 在列表中把刚才添加的内容加上去;如果失败,则提示。可是用了
UpdatePanel:它首先将列表、添加、修改、删除的 TextBox
等内容全部传回服务器端,服务器端处理了添加操作后,又从数据库中查找记录组成列表,最后又整个返回客户端,使得速度很慢。

UpdaePanel 的局部呈现

       UpdaePanel 能让你使用服务器端逻辑处理普通页面并以无闪动的 Ajax
风格刷新自己。

       基本思想是把 Web
页面分解为一到多个独立的区域,其中每个区域都包含在一个不可见的
UpdaePanel 里。当 UpdaePanel
中发生通常会触发一次回发的事件时,UpdaePanel
截取该回发事件代之以执行一次异步回调

       下面是它发生过程的一个示例步骤:

  1. 用户单击 UpdaePanel 里的一个按钮
  2. 一些客户端 js 代码(由 ASP.NET AJAX
    生成)截取到单击事件并执行一次服务器回调
  3. 在服务器端,页面周期正常执行,所有常规事件正常发生。
  4. 最终,页面被呈现为 HTML 返回浏览器
  5. 客户端 js 得到完整的 HTML 并更新页面上所有的 UpdaePanel
    (如果变更的内容不在 UpdaePanel 里,就会被忽略)

 

       UpdaePanel 控件和 ScriptManager 控件一起工作。在使用 UpdaePanel
前,需要把 ScriptManager.EnablePartialRendering 属性设置为
true(默认值),然后才可以向页面添加 UpdaePanel 控件(放在 ScriptManager
之后)。

       把控件拖到 UpdaePanel 时,内容出现在 <ContentTemplate>
节,如下:

<asp:ScriptManager ID="ScriptManager1" runat="server" EnablePartialRendering="true">

</asp:ScriptManager>

<asp:UpdatePanel ID="UpdatePanel1" runat="server">

    <ContentTemplate>

        <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>

        <asp:Button ID="Button1" runat="server" Text="Button" />

    </ContentTemplate>

</asp:UpdatePanel>

       UpdaePanel 是一个基于模板的控件。呈现自身时,它把自己的
ContentTemplate 节的内容复制到页面上,因此,不能通过 UpdaePanel.Controls
集合动态添加控件,而要通过 UpdaePanel.ContentTemplateContainer.Controls
集合,这点请注意

 

       UpdaePanel 并非继承自 Panel,而是直接继承
Controls。在其生命周期里只有一个角色:作为异步刷新内容的容器。UpdaePanel
没有可视界面,不支持样式设置。如果要在 UpdaePanel
之外显示边框或者修改背景颜色,那么需要在 UpdaePanel 里放置一个普通的
Panel 或 DIV 标签

 

       UpdaePanel 把自身呈现为 <div> 标签。但如果设置 RenderMode
属性由 Block 改为 Inline,可以配置 UpdaePanel
自身呈现为嵌入元素
。如果要创建的 UpdaePanel
处于段落中或者其他块元素中,就可以进行这样的设置。

 

       下图显示包含 3 个 UpdaePanel 控件的示例页面,每个 UpdaePanel
都包含相同的内容:一个 Label 和一个 Button
控件。每次页面传送到服务器时,Page.Load 事件触发:

protected void Page_Load(object sender, EventArgs e)

{

    Label1.Text = DateTime.Now.ToLongTimeString();

    Label2.Text = DateTime.Now.ToLongTimeString();

    Label3.Text = DateTime.Now.ToLongTimeString();

}

澳门新葡萄京官网注册 1

       这个页面显示了异步回调无闪动刷新,单击任一按钮,3
个标签都会安静的更新。唯一的例外是浏览器不支持 XMLHttpRequest
对象时,UpdaePanel 会自动降级为使用完整页面回发。

 

  在第一个检测昵称的按钮点击时会调用一个js方法:check(),在check()函数中,我们首先去获取TextBox中用户输入的名称,然后将名称传入js调用的WebService方法中进行操作,得到结果后再根据结果去设置div要显示的内容,如果存在则返回值为false。这里有一个js回调函数的概念:回调函数用于在异步操作中扩展第一个函数的功能,我们此处是为了在返回结果之后设置div的innerHTML的值。如果不使用回调函数而直接使用同步的话,可能会不执行函数的内容。

但是 UpdatePanel 也不是万能的,在使用时还是需要慎重的。原因就是 ASP.NET
会传送所有 UpdatePanel 中的数据,如果这些 UpdatePanel
中的数据很大,就会造成速度缓慢。

使用 Timer 定时刷新

      
有时候可能希望用户没有动作的情况下强制完成一次完整或部分页面的刷新。例如一个股价的页面,你会希望定期刷新股价区域。ASP.NET
AJAX 的 Timer 控件可以实现这种设计。

       Timer 控件的使用非常简单,只要把它的 Interval
属性设置为页面更新间隔的最大毫秒数即可。不过要知道,Timer
控件可能会大大增加 Web 应用程序的负载并降低它的可扩展性。

       Timer 控件会引发服务器端 Tick
事件,可以在该事件中更新页面。但此事件的使用也不是强制的,因为定时器触发时会执行完整的页面生命周期,也可以响应页面和其他控件事件,比如
Page.Load 。

       要在部分呈现中使用 Timer 控件,可把页面的可更新部分包装到
UpdatePanel 控件并设置 UpdateMode 为
Conditional,定时器触发时会强制更新:

<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">

    <ContentTemplate>

    ...

    </ContentTemplate>

    <Triggers>

        <asp:AsyncPostBackTrigger ControlID="cmdOutsideUpdate" EventName="Tick" />

    </Triggers>

</asp:UpdatePanel>

<asp:Timer ID="Timer1" runat="server" Interval="60000" ontick="Unnamed1_Tick"></asp:Timer>

      
页面的其他部分保持不变,或者如果要响应其他动作而更新它们,可以把它们包装到具有不同触发器的条件更新的
UpdatePanel 控件里。

       如果要停止一个定时器,只要在服务器端代码里设置 Enabled 属性为
false 即可。例如下面的代码在 10 次更新后禁用定时器:

protected void Unnamed1_Tick(object sender, EventArgs e)

{

    int tickCount = 0;

    if (ViewState["TickCount"] != null)

    {

        tickCount = (int)ViewState["TickCount"];

    }

    tickCount++;

    if (tickCount > 10)

    {

        Timer1.Enabled = false;

    }

}

 

 

  

所以我们在使用 UpdatePanel
时,想想我们的数据量有多大,是不是需要把多个功能拆分到多个页面?

5. 优化 UpdatePanel

       UpdatePanel 有时会因带宽因素让人有不好的印象。这是因为
UpdatePanel 几乎总是传输超过你所需要的信息量

      
假设你创建一个页面用于显示一张表。实现这个页面最有效的办法就是使用
ASP.NET AJAX 和 Web 服务。页面会调用服务器端的一个 Web
服务获取数据,你需要编写客户端的 js 代码并将它转换为 HTML。

       与上面的办法相比,类似的解决方案是使用一个带有富数据控件的
UpdatePanel,比如
GridView。这个方案让你可以避免写大部分的代码,但是,UpdatePanel
需要请求更多信息来刷新自身。即 Web 服务器需要发送完整的 GridView
呈现内容,和 UpdatePanel
中其他控件的呈现内容以及页面的完整视图状态。这些信息远多于你采用 Web
服务方式所需的信息量。

       为了获得最佳的 UpdatePanel
性能,了解它的本质是必须的。一些最佳的做法如下:

  • 尽可能的压缩视图状态数据,使用 EnableViewState
    属性关闭那些有变化内容但是不需要用视图状态保存的控件的视图状态。
  • 在 UpdatePanel 里只放置最少的内容,如上例中,只因放
    GridView,而使用触发器特性将其他可能触发更新的控件放置在 UpdatePanel
    之外。
  • 如果页面中有多个可更新区域,应放置在独立的 UpdatePanel 中,并设置
    Conditional 模式。Web 服务器应答一个回调时,将呈现的标记发送至
    UpdatePanel 和页面中所有非 Conditional 的
    UpdatePanel。如果你仅仅需要为其中一个面板获取新内容,那就不需要为其他的面板获取内容。
  • 在使用了多个更新面板的复杂页面中,考虑用手动的方式刷新它们。要做到这一点,将面板设置为
    Conditional 并将 ChildrenAdTriggers 属性设置为
    false。现在唯一能引发刷新动作的就是在一个回调请求期间在一个或更多
    UpdatePanel 控件上显式调用 Update() 方法。

 

       UpdatePanel
方式永远不会比页面回发的方式差,因为页面回发方式总是将整个页面进行回发。UpdatePanel
方式仅仅在与手工进行 ASP.NET AJAX 编程相比时显得不够好

 

 

     
接下来是WebService,在建立WebService时,需要注意将[System.Web.Script.Services.ScriptService]这行代码的注释取消掉,VS2010中建立后会提示使用ASP.NET

       由于这个原因,ASP.NET
提供了一个更高层的服务器模型,它提供可以直接在 Web
窗体里使用的控件和组件,你可以完全使用服务器端代码工作

  我们通过一个简单的Demo重现了ASP.NET
原生控件实现Ajax的功能,这也体现了ASP.NET本身封装的强大之处,同时使用起来也很方便。但是由于ScriptManager和UpdatePanel控件本身为服务器端控件,在一定程度上会生成一些垃圾代码,而且在请求时会生成整个页面的控件树,因此在批量使用的情况下会导致资源的占用率很高,这也是它本身的缺点。下次我会为大家带来Ajax的另一种实现方式:JQuery自带的ajax()方法结合ashx一般处理程序实现,这是一种轻量级的实现方式。

使用 UpdateProgress 的耗时更新

       ASP.NET AJAX 还包括 UpdateProgress 控件,它和 UpdatePanel
的部分呈现一起工作。UpdateProgress
控件的名称不太准确。它并不指示进度,而是提供一条等待信息让用户知道页面还在工作,最后的请求还在继续处理中。

       添加 UpdateProgress
控件后,就能够指定异步请求开始后显示某些内容,而这些内容在异步请求结束时又将自动消失。这些内容可以包括固定的消息或图片。通常,会用一个动画
GIF 来模拟进度条。

       下面是一个使用了 UpdateProgress 控件的页面在其生命周期 3
个不同阶段的画面:

澳门新葡萄京官网注册 2
澳门新葡萄京官网注册 3
澳门新葡萄京官网注册 4

       页面标记中定义了一个 UpdatePanel,随后跟着一个  UpdateProgress
。UpdateProgress 控件包含一个取消按钮,稍后会详细介绍它:

<asp:UpdatePanel ID="UpdatePanel1" runat="server">

    <ContentTemplate>

        <div style="background-color: #FFFFE0; padding: 20px">

            <asp:Label ID="lblTime" runat="server" Font-Bold="True"></asp:Label>

            <br />

            <br />

            <asp:Button ID="cmdRefreshTime" runat="server" Text="Start the Refresh Process" 

            OnClick="cmdRefreshTime_Click" />

        </div>

    </ContentTemplate>

</asp:UpdatePanel>

<br />

 

<asp:UpdateProgress runat="server" ID="updateProgress1">

    <ProgressTemplate>

        <div style="font-size: xx-small">

            Contacting Server ...<img src="wait.gif" alt="Wait" />

            <input id="Button1" onclick="AbortPostBack()" type="button" value="Cancel" 

            style="font-size: xx-small;" />

        </div>

    </ProgressTemplate>

</asp:UpdateProgress>

 

       为了模拟一个较慢的过程,可添加一行延时 10 秒的代码:

protected void cmdRefreshTime_Click(object sender, EventArgs e)

{

    System.Threading.Thread.Sleep(TimeSpan.FromSeconds(10));

    lblTime.Text = DateTime.Now.ToLongTimeString();

}

       不需要把 UpdateProgress 控件显式关联到 UpdatePanel
控件。无论哪一个 UpdatePanel 开始回调,UpdateProgress 都会自动显示它的
ProgressTemplate。
不过,如果页面很复杂且具有多个
UpdatePanel,可以选择让 UpdateProgress
只关注其中某一个。要这么做的话,只要把
UpdateProgress.AssociatedUpdatePanelID 的属性设为相应的 UpdatePanel 的
ID 即可
。甚至可以为同一页面添加多个 UpdateProgress
控件,然后分别关联到不同的 UpdatePanel。

 

取消

       UpdateProgress
控件还支持另一个细节,取消按钮。用户单击取消时,异步回调就会立即被取消,UpdateProgress
的内容将消失,页面重新回到它的原始状态。

       添加取消按钮是一个两步过程。首先,要添加一段执行取消的 js
代码。下面的代码执行这一工作(必须放在 ScriptManager 控件之后):

var prm = Sys.WebForms.PageRequestManager.getInstance();

prm.add_initializeRequest(InitializeRequest);

 

function InitializeRequest(sender, args) {

    if (prm.get_isInAsyncPostBack()) {

        args.set_cancel(true);

    }

}

 

function AbortPostBack() {

    if (prm.get_isInAsyncPostBack()) {

        prm.abortPostBack();

    }

}

       添加这段代码后,就可以随时通过 JavaScript 代码调用
AbortPostBack() 函数取消回调:

<input id="Button1" onclick="AbortPostBack()" type="button" value="Cancel" />

       典型情况下,会把这个按钮(或者类似的元素)放到 UpdateProgress
控件的 ProgressTemplate 里,因为它只在回调进行时才被应用。

 

      
对那些能够被安全取消的任务提供取消按钮是有意义的,因为它们不影响外部的状态。例如,用户应该能够取消耗时的查询。但是,为更新操作提供取消则不是什么好主意,因为服务器会持续执行直到它结束更新,即使客户端已经停止监听响应时也是如此。

 

 

  我们可以看到数据库中已经存在如下11条昵称,在运行时我们输入“豆豆”并点击检测昵称按钮,会发现代码进入到WebSerivce中:

1. 错误处理

       UpdaePanel 执行异步回调后,Web
页面代码完全按页面被回发时那样运行。唯一的区别在于:

  • 通信方式:页面通过一个 XMLHttpRequest 异步调用获取新数据
  • 接收到的数据的处理方式:UpdaePanel
    刷新自身内容,页面其余部分保持不变

      
同步回发中可能发生的问题也完全会在执行异步回发时发生,添加下列代码测试:

if (IsPostBack)

    throw new ApplicationException("This operation failed.");

       Web 页面抛出一个未处理的异常时,ScriptManager
捕获这个错误并回传给客户端,然后 ASP.NET AJAX 客户端库在页面中抛出一个
JavaScript
错误。下一步发生什么取决于浏览器的设置。如果启用了脚本调试,VS
会在产生错误的那一行中断。如果没有使用脚本调试,浏览器可能通知也可能不通知你发生了一个问题。现在大多数浏览器都被配置为忽略
JavaScript 错误。对于 IE,会在状态栏左下角出现“页面错误”消息。

澳门新葡萄京官网注册 5
澳门新葡萄京官网注册 6

 

       使用客户端 js 处理错误可以改变这一行为。为此,你需要为
System.Web.PageRequestManager 类的 endRequest
事件注册一个回调
PageRequestManager 是 ASP.NET AJAX
应用程序模型的核心部分,它管理 UpdaePanel
控件的刷新过程并在页面的生命周期各个阶段触发客户端事件

       下面这段客户端脚本块就是这样注册的:

  1. 定义页面第一次加载时触发的函数
  2. 不需要使用 onload 事件,只要有 pageLoad() 函数,ASP.NET AJAX
    就会自动调用它
  3. 不需要使用 unload 事件,只要有 pageUnload() 函数,ASP.NET AJAX
    也会自动调用它
  4. 除了上述 2 个函数,其余函数均须手工绑定

       下面是 pageLoad() 函数,它得到 PageRequestManager
当前实例的引用,并向 endRequest 事件附加第二个函数:

function pageLoad() {

    var pageManager = Sys.WebForms.PageRequestManager.getInstance();

    pageManager.add_endRequest(endRequest);

}

       endRequest 事件在每次异步回发结束时发生。本例中,endRequest()
函数检查是否有错误发生。如果有,把错误信息显示到另一个控件里,并调用
set_errorHandled() 方法来消除 ASP.NET AJAX
标准错误处理行为(显示错误消息框)

function endRequest(sender, args) {

    // Handle the error.

    if (args.get_error() != null) {

        $get("lblError").innerHTML = args.get_error().message;

 

        // Suppress the message box.

        args.set_errorHandled(true);

    }

}

       下图显示了使用错误处理后的界面效果:

澳门新葡萄京官网注册 7

 

       ASP.NET 里有 2 个不能在 UpdatePanel 中使用的控件:FileInput 和
HtmlInputFile 控件。但它们仍可以在含有 UpdatePanel 控件的页面中使用

 

  ASP.NET原生控件用于Ajax技术的主要是UpdatePanel和ScriptManager,前者顾名思义,是一个可以用于盛放内容的容器,用于实现页面的局部更新,在使用的时候直接将需要更新的内容放入即可。后者用于调用一些服务和脚本:例如我们本次Demo中使用JavaScript调用WebService服务。

3. 被中断的更新

      
有一点需要说明,如果执行的是一个很耗时的更新,那么更新可能会被其他更新中断。ASP.NET
AJAX
异步回发页面,因此用户能够在回发进行过程中继续单击页面的其他按钮。ASP.NET
AJAX 不允许并发更新,因为它需要确保其他信息(如视图状态、会话 cookie
等)保持一致
。当一个新的异步回发被启动后,前一个异步回发就被取消了。

      
大部分情况下,这正是你希望的行为。但为了防止用户打断进行中的异步回发,可以通过
js 代码在异步回发的过程中禁用控件。你需要为 beginRequest
事件附加一个事件处理程序,这和错误处理程序示例中给 endRequest
事件添加处理程序一样。

 

  和我们意料之中相同,“豆豆”这个昵称已经在数据库存在,接下来我们看看最终的结果:

 


4. 触发器

      
如果使用的是条件更新模式,还有其他一些办法来触发更新。其中一个办法就是使用触发器告诉
UpdatePanel 在页面上的某个特定控件的特定事件发生时来呈现自身。

       从技术上而言,UpdatePanel 总是使用触发器。UpdatePanel
中所有控件自动成为 UpdatePanel 的触发器
。在先前的示例中,当
Button.Click 事件发生时,就会发生一次异步回发。然而,这同时也能和所有
Web 控件的默认事件(控件里用 DefaultEvent
特性标记的事件)一起工作,只要那个事件回发了页面。例如,在 UpdatePanel
里放置一个 TextBox,设置 AutoPostBack 属性为 True,那么
TextBox.TextChanged 事件就会触发一次异步回发,同时 UpdatePanel
也将被更新。

       触发器可以按两种方式改变这个行为。首先,它允许把触发器关联到
Panel
之外的某个控件上。例如页面其他位置有一个按钮,这个按钮本将触发一次完整的回发,但把它关联到
UpdatePanel 之后,它将执行一次异步回发。要实现这一设计,只需给
UpdatePanel 增加 AsyncPostBackTrigger,指定要监听的控件的 ID
以及要触发刷新的事件:

<div>

    <asp:ScriptManager ID="ScriptManager1" runat="server">

    </asp:ScriptManager>

    <br />

    <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">

        <ContentTemplate>

            <div style="background-color: #FFFFDD; padding: 20px">

                <asp:Label ID="Label1" runat="server" Font-Bold="True"></asp:Label>

            </div>

        </ContentTemplate>

        <Triggers>

            <asp:AsyncPostBackTrigger ControlID="cmdOutsideUpdate" EventName="Click" />

        </Triggers>

    </asp:UpdatePanel>

    <br />

    <asp:Button ID="cmdOutsideUpdate" runat="server" Text="Update" />

</div>

       EventName
指定要监听的事件,通常不需指定,默认监听控件的默认事件,不过,显式设置是一个好的选择

       现在单击按钮,单击在客户端被拦截,并且 PageRequestManager
执行一次异步回发,会发生如下情况:

  • 所有 UpdateMode 设置为 Always 的 UpdatePanel 控件都会被刷新
  • 所有 UpdateMode 设置为 Conditional 并且有针对 cmdOutsideUpdate 的
    AsyncPostBackTrigger 的 UpdatePanel 控件都会被刷新
  • 所有 UpdateMode 设置为 Conditional 但不监听按钮的 UpdatePanel
    不会被刷新

       注意:

  • 为同一 UpdatePanel 添加多个触发器,其中任一事件都将触发更新
  • 将同一触发器添加到多个 UpdatePanel,此时一个事件同时更新所有
    UpdatePanel
  • 在条件更新的 UpdatePanel
    里混合匹配触发器和内嵌控件,这样内嵌控件的所有事件及触发器里的事件都将引发更新

 

       还可以按另一种方式使用触发器。假设 UpdatePanel
里有一个按钮,单击将触发一次异步请求和局部更新。如果希望触发一次完整的页面回发,只要添加
PostBackTrigger 即可

<asp:ScriptManager ID="ScriptManager1" runat="server">

</asp:ScriptManager>

<br />

<asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode="Conditional">

    <ContentTemplate>

        <div style="background-color: #FFFFDD; padding: 20px">

            <asp:Label ID="Label1" runat="server" Font-Bold="True"></asp:Label>

            <br />

            <br />

            <asp:Button ID="cmdOutsideUpdate" runat="server" Text="Update" />

        </div>

    </ContentTemplate>

    <Triggers>

        <asp:PostBackTrigger ControlID="cmdOutsideUpdate" />

    </Triggers>

</asp:UpdatePanel>

       这项技术应用不太常见。但是当 UpdatePanel
中的多个控件只执行很少的更新(因此使用异步回发),而有一个控件执行整个页面的大片更新(因此使用完整回发)时它就很有用了

 

Ajax从脚本中调用此Web服务时就需要这样,否则会调用不到该方法。WebService中代码如下:

       在这篇文章里,我将介绍 ASP.NET 框架中的 3 个 ASP.NET AJAX
控件:UpdatePanel、Timer、UpdateProgress。所有这些控件都支持局部呈现,可以不经过完整回发无缝地更新页面内容,这是
Ajax 的关键概念。

  我们在Web开发中经常会接触到Ajax技术,同时Ajax技术也有很多种实现方式,那么,我们今天从第一种方式说起:ASP.NET原生控件实现Ajax。

       ASP.NET AJAX 控件会自动注入客户端脚本,在幕后使用 ASP.NET AJAX
脚本库。与手动编写 js 代码相比,潜在的缺点是降低了灵活性。

  澳门新葡萄京官网注册 8

       ASP.NET AJAX 对 Web
服务特性为客户端代码访问服务器提供了一个可贵的窗口。但你要做大多数的困难任务,必须精心打造
Web 方法、合适的时机调用它们、适当更新页面、除了 JavaScript
什么都不能用。对于复杂的呈现,这个过程就十分单调。

  

管理浏览器历史

       每次页面执行一次全面回发,Web
浏览器就会将其作为一个页面导航来处理,并在历史列表中加入一个新项。然而,当你使用
UpdatePanel 来执行异步回发的操作时,历史列表不会被更新。
这一缺点在
ASP.NET AJAX
页面执行一个复杂的多步骤操作的时候变得尤为明显。如果用户稍不注意单击了“后退”按钮回退到前一步的话,浏览器会跳回到前一个页面,并且刚才用户所做的所有工作都会丢失。

       如果已经安装了 ASP.NET 4,那么有一个合适的解决方法。通过使用
ScriptManager,你可以控制浏览器的历史列表。你可以在需要的时候新增浏览器列表项,并在用户单击“后退”或“前进”时作出响应,确保页面状态是被正确恢复的。

       事实上,与普通 Web
表单的导航相比,这个处理过程工作的相当好,因为不会提示用户重新填写之前页面上的信息。如同大多数
ASP.NET AJAX
特性一样,历史列表支持特性给了你一个很好的方式来获得这些功能,而无需你自己来编写恼人的复杂代码并且无需考虑浏览器的兼容性。

 

       为了看到 ScriptManager 的浏览器历史特性是如何工作的,可以使用
Wizard 控件来创建一个简单的例子。下面是一个分为 3
步向导的简化例子,Wizard 控件包含在 UpdatePanel 中:

<asp:UpdatePanel ID="UpdatePanel1" runat="server">

    <ContentTemplate>

        <asp:Wizard ID="Wizard1" runat="server" ...>

            <WizardSteps>

                <asp:WizardStep runat="server" Title="Step 1">

                    This is Step 1.

                </asp:WizardStep>

                <asp:WizardStep runat="server" Title="Step 2">

                    This is Step 2.

                </asp:WizardStep>

                <asp:WizardStep runat="server" Title="Step 3">

                    This is Step 3.

                </asp:WizardStep>

            </WizardSteps>

            ...

        </asp:Wizard>

    </ContentTemplate>

</asp:UpdatePanel>

       这个页面充分利用了 ASP.NET AJAX
的优势。现在单击向导中的链接进行导航,页面不会出现山东。然而,历史列表也不会有任何变化。

 

1. 新增历史点

       首先,将 ScriptManager.EnableHistory 属性设为
true,这将允许使用 ScriptManager 来新增历史点并对历史导航作出响应。

       接下来,需要在服务器端代码中调用
ScriptManager.AddHistoryPoint()
方法将条目加入到历史列表中。并需要提供下面 3 个参数

  • State:状态值是一个字符串,用来存储与历史点相关的信息。当用户返回到这个历史点时,可以获取到相关的状态。比如在这个例子中,它会存储向导的当前步骤索引。
  • Key:键值是一个唯一的字符串名字,用来存储状态信息。允许页面存储多个不冲突的状态值。这对于拥有多个使用历史列表的控件的情况很有用。比如,你有两个
    Wizard 控件在同一页面中,只要每一个 Wizard
    都使用了一个不同的键值,就可以同时存储各自当前步骤的索引。
  • Title:页面标题显示在浏览器窗口顶部并被记录在历史列表中。你也可以省略标题参数,此时历史点将会使用当前的页面标题。

      
本例中,在控件更改到一个新步骤时新增一个历史点是比较合适的。在向导的
ActiveStepChanged 事件中来做这个动作:

protected void Wizard1_ActiveStepChanged(object sender, EventArgs e)

{

    if ((ScriptManager1.IsInAsyncPostBack) && (!ScriptManager1.IsNavigating))

    {

        string currentStep = Wizard1.ActiveStepIndex.ToString();

        ScriptManager1.AddHistoryPoint("Wizard1", currentStep, "Step " + (Wizard1.ActiveStepIndex + 1).ToString());

    }

}

      
在新增历史点之前,代码要检测两个细节。首先检测是不是作为一个异步回调操作的一部分进行变更的。

       这可以确保不会在下列情况下新增历史点:

  • 页面首次被创建且属性 ActiveStepIndex 第一次被赋值
  • 使用“前进”或“后退”按钮时,IsNavigating 属性为 true

       上述情况发生的时候,不需要存储状态,相反,需要通过处理
ScriptManager.Navigate 事件来还原它(后面会介绍)。

       当新增历史点时,代码使用索引名字
Wizard1(匹配控件的名称)存储当前步骤的索引并将页面标题设置为一个说明性字符串,比如“Step
1”。如果现在运行页面会看到历史表中出现新的项,然而单击一个历史项则什么也不会发生,这是因为还没有编写代码来还原页面状态。

 

2. 还原页面状态

       当用户在历史列表中前后移动时,ScriptManager
执行一个异步回调来刷新页面。这时可以处理 ScriptManager.Navigate
事件。当处理 Navigate 事件时,由你决定使用 HistoryEventArgs.State
集合来获取需要的状态以及当你首次新增历史点的时候所使用的状态值,表明用户可能返回到了页面的第一个书签,这意味着你应当返回到向导控件的初始状态:

protected void ScriptManager1_Navigate(object sender, HistoryEventArgs e)

{

    if (e.State["Wizard1"] == null)

    {

        // Restore default state of page (for exmaple, for first page).

        Wizard1.ActiveStepIndex = 0;

    }

    else

    {

        Wizard1.ActiveStepIndex = Int32.Parse(e.State["Wizard1"]);

    }

    Page.Title = "Step " + (Wizard1.ActiveStepIndex + 1).ToString();

}

       这段代码同时更新了页面标题以与历史记录相匹配,因为 ScriptManager
不会自己执行这个操作。

       现在向导控件已经能够很好的工作了:

澳门新葡萄京官网注册 9

 

3. 状态是如何在 URL 中存储的

       事实上,ScriptManager 将状态值放在了页面的 URL 里。它使用
Base64 编码来混淆这些值并在最后使用散列码来做校验
。下面是向导例子中的
URL:

       状态信息被放置在 URL 符号 #
之后。因此不会打乱任何正在使用的查询字符串参数,这些参数出现在 #
号前。

 

      
历史状态使用与视图状态同样的编码机制。这意味着用户能毫不费力的获取到你的状态值,但是用户并不能篡改这些值,因为他们不能在没有
Web 服务器私钥的情况下产生一个正确的散列码。

      
和视图状态不一样的地方是,你不能加密状态值。然而,如果你想更清晰的查看
URL,可以移除这些编码和散列码。只需设置
ScriptManager.EnableSecureHistoryState 为 false 即可。然后 URL
看起来就像下面这样:

       当然,这就完全使得用户可以通过修改 URL 来更改一个状态值。


  此时说明js脚本调用WebService方法已经成功,接下来会执行通用操作类中的方法:

  也已经达到了我们预期的效果。由于局部刷新的图不太容易截到,这里就无法为大家呈现了。我们大家都知道服务器端Button在点击时都会出现响应回发,因此点击另外一个刷新页面的按钮,页面会有明显的闪烁(整个页面全部刷新),这也体现了Ajax技术带来的优势,提升了用户的体验度。

  我在这个页面中放入了一个ScriptManager,同时引用了一个WebService,然后将需要更新的部分放入了UpdatePanel。此外还有两个Button,第一个用于检测昵称,第二个用于刷新页面。大家看过代码之后会发现我在UpdatePanel设置时加入了一个Triggers,那么这个是干什么的呢?获取或设置触发对
UpdatePanel
控件进行更新的回发控件事件。可以看到,我将检测昵称的Button设置为AsyncPostBackTrigger模式,此为点击按钮时不刷新整个页面,只刷新UpdatePanel中的内容,而第二个按钮设为PostBackTrigger模式,则会刷新整个页面。

using System;  using System.Collections.Generic;  using System.Linq;  using System.Web;  using System.Data;  using System.Data.SqlClient;    namespace WebApplication1  {      public class userHelper      {              public static bool CheckName(string Name)          {              string Con = @"Data Source=205ZHANGXKSQLEXPRESS;Initial Catalog=MyQQ;Connect Timeout=30;Trusted_Connection=true";              using (SqlConnection sqlCon = new SqlConnection(Con))              {                  string Sql = "select count(*) from users where NickName='" + Name + "'";                  sqlCon.Open();                  SqlCommand cmd = sqlCon.CreateCommand();                  cmd.CommandText = Sql;                  int res = int.Parse(cmd.ExecuteScalar().ToString());                  if (res == 1)                  {                      return false;                  }                  else                  {                      return true;                  }                               }          }      }  }

  澳门新葡萄京官网注册 10

  首先,我们需要建立一个Web页面(此处选用WebForm),大致代码如下:

  澳门新葡萄京官网注册 11澳门新葡萄京官网注册 12

  在页面上已经出现了我们预期的结果,我们再来试试输入另外一个不存在的昵称:

  澳门新葡萄京官网注册 13澳门新葡萄京官网注册 14

  本次Demo主要包含一个页面,一个Web服务(WebService)和一个数据通用操作类(userHelper)来实现页面无刷新检测注册时昵称是否存在的功能。

  我在操作时连接了本地的数据库MyQQ,该方法实现即为查找名称,然后返回受影响的行数并返回结果。下面我们开始调试代码:

  

using System;  using System.Collections.Generic;  using System.Linq;  using System.Web;  using System.Web.Services;    namespace WebApplication1  {      /// <summary>      /// WebService1 的摘要说明      /// </summary>      [WebService(Namespace = "http://tempuri.org/")]      [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]      [System.ComponentModel.ToolboxItem(false)]      // 若要允许使用 ASP.NET AJAX 从脚本中调用此 Web 服务,请取消对下行的注释。      [System.Web.Script.Services.ScriptService]      public class WebService1 : System.Web.Services.WebService      {            [WebMethod]          public string HelloWorld()          {              return "Hello World";          }          [WebMethod]          public bool CheckName(string Name)           {              return userHelper.CheckName(Name);          }      }  }

  澳门新葡萄京官网注册 15

  我们此处调用的CheckName方法,源自于数据通用类中的CheckName方法,userHelper中实现方法代码如下:

发表评论

电子邮件地址不会被公开。 必填项已用*标注