Spiga

UpdatePanel的妙用:Incremental Content

2007-03-29 20:18 by Jeffrey Zhao, 5619 visits, 网摘, 编辑

  Incremental Content是我随意取的名字,我有时候会希望,把一些常见的场景,总结出ASP.NET AJAX一些比较固定的使用模式。Incremental Content是我为现在这个“模式”取的名字。这个模式的作用,就是使用UpdatePanel来不断地在页面上增加内容。

  想到这个使用方式的原因,是因为在中午与Bing对UpdatePanel的一些问题进行了讨论。他谈到,使用UpdatePanel,会造成过多的数据传输上的浪费。例如博客园的回复,事实上要更新的只是新增的内容,而并不需要整个评论区进行刷新。

  是啊,说的没错。我当时的回答是:“这是因为UpdatePanel的设计初衷,可以很方便地为页面带入AJAX效果”。但是这并不能为UpdatePanel带来的这个问题进行开脱。不过在我简单进行了思考之后,发现要让UpdatePanel支持内容的递增效果并不困难。我们只要灵活地运用UpdatePanel的工作机制即可。

  UpdatePanel在更新时,最后从服务器端得到的是什么呢?事实上得到的是需要更新的UpdatePanel的ID,以及UpdatePanel中的内容。接下去作的事情自然不必多说,即使通过ID找到UpdatePanel所在的div(或者span),然后替换其innerHTML。

  等等,有没有想到什么?客户端是如何找到需要更新的UpdatePanel?“通过ID”。既然是通过ID,我们为什么不能让UpdatePanel更新到我们指定的区域呢?我们当然可以这么做,下面的示例就是这样实现UpdatePanel的Incremental Content的。

  首先,在页面上放置一个UpdatePanel,在它的ContentTemplate中,我们使用一个Visible为False的Panel来包装了内容,它的作用,可以说是为了给每次添加的内容设定一个模版。如下:

Default.aspx
<div id="inputContainer">
    <asp:UpdatePanel ID="upAppendContent" runat="server">
        <ContentTemplate>
            <strong>
                您在<asp:Literal runat="server" ID="time" />输入了:
            </strong>
            <br />
            <asp:Literal runat="server" ID="appendMessage"></asp:Literal>
            <hr />
        </ContentTemplate>
        <Triggers>
            <asp:AsyncPostBackTrigger ControlID="btnAppend" />
        </Triggers>
    </asp:UpdatePanel>
</div>

<br /><br />

<asp:TextBox ID="txtAppendMessage" runat="server"></asp:TextBox>
<asp:Button ID="btnAppend" runat="server" Text="btnAppend" OnClick="btnAppend_Click" />

 

  然后,我们在每次用户点击按钮之后,我们需要将UpdatePanel中的控件内容进行修改。请注意,在Form_Load方法中,需要在非异步刷新的情况下,将UpdatePanel中的内容清空,这样避免了在第一次加载页面时UpdatePanel中出现内容。如下:

Default.aspx.cs
protected void Page_Load(object sender, EventArgs e)
{
    if (!ScriptManager.GetCurrent(this).IsInAsyncPostBack)
    {
        this.upAppendContent.ContentTemplateContainer.Controls.Clear();
    }
}

protected void btnAppend_Click(object sender, EventArgs e)
{
    this.time.Text = DateTime.Now.ToString("HH时mm分ss秒");
    this.appendMessage.Text = HttpUtility.HtmlEncode(this.txtAppendMessage.Text);
}

 

  如果到这里为止,相信大家能够猜到执行的效果是怎么样的:每次提交后,UpdatePanel中的信息被更改。这并不是我们需要的Incremental Content的效果。因此,我们还需要加一些JavaScript进行辅助。如下:

Default.aspx
<script type="text/javascript" language="javascript">
    Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function(sender, e)
    {
        var upID = "<%= this.upAppendContent.ClientID %>";
        var refreshedPanels = e.get_panelsUpdated();
        
        for (var i = 0; i < refreshedPanels.length; i++)
        {
            if (refreshedPanels[i].id == upID)
            {
                refreshedPanels[i].id = upID + Math.floor(9999 * Math.random());
                
                var div = document.createElement("div");
                div.id = upID;
                $get("inputContainer").appendChild(div);
                return;
            }
        }
    });
</script>

 

  这段代码才是Incremental Content的关键所在。这这段代码中,我们响应了客户端的pageLoaded事件,也就是说,我们在UpdatePanel更新完毕之后,对于刷新的UpdatePanel进行遍历。如果发现我们的目标UpdatePanel被更新了,则改变其ID,并新建一个div容器,添加到页面中合适的地方。这样,UpdatePanel在下次进行更新时,就会将我们新建的容器作为客户端的UpdatePanel,并为其设置innerHTML。

  这就是Incremental Content。我们可以看到,服务器端每次回传的数据,只会是我们新增的内容,而我们每次提交的内容,都会被保留在页面上。如下:

Incremental Content

 

  Incremental Content的好处是什么呢?减少数据传输量是一方面。另外,如果使用Incremental Content,一般来说查询数据库的次数就能减少,这样就可以进一步降低资源消耗。

 

点击这里下载代码。

点击这里查看效果。

 

PS:

  建议博客园能够使用Incremental Content方法。由于博客园存在“刷新全部评论”的操作,所以可以使用UpdatePanel嵌套的方式,对内UpdatePanel使用Incremental Content,而刷新全部评论时,则可以调用外部UpdatePanel的Update方法(或通过Trigger指定)。不过一旦使用了Incremental Content方法,在“修改评论”和“删除评论”两个功能上,可能就要改变实现方式了。

  这是我的一点建议,供dudu参考。:)

Add your comment

33 条回复

  1. #1楼 上网助手[未注册用户]2007-03-29 20:35
    收藏了。。
      回复  引用    
  2. #2楼 Dflying Chen      2007-03-29 20:38
    @上网助手
    广告真多啊……
      回复  引用  查看    
  3. #3楼 test[未注册用户]2007-03-29 20:50
    test ajax请删除~
      回复  引用    
  4. #4楼 哈密瓜牌牛奶      2007-03-29 21:36
    我也来了,学习中:)
      回复  引用  查看    
  5. #5楼 大剑师      2007-03-29 22:17
    哈哈,有创意,有启发
      回复  引用  查看    
  6. #6楼 edrp.cn      2007-03-29 22:19
    今天没有时间听课,什么时候可以下载?
      回复  引用  查看    
  7. #7楼 ※ABeen※      2007-03-29 22:30
    真好用!不过不明白Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded
    这是什么意思?
      回复  引用  查看    
  8. #8楼[楼主] Jeffrey Zhao      2007-03-29 22:32
    @edrp.cn
    ppt和Demo已经可以在我的blog上下载到.
      回复  引用  查看    
  9. #9楼[楼主] Jeffrey Zhao      2007-03-29 22:33
    @※ABeen※
    在客户端相应pageLoaded事件,这时候UpdatePanel已经更新好了。
      回复  引用  查看    
  10. #10楼 Cat Chen      2007-03-29 23:26
    这东西不错,在某些情境下会非常有用。
      回复  引用  查看    
  11. #11楼 Clark Zheng      2007-03-30 01:40
    一直关注!
      回复  引用  查看    
  12. #12楼 MK2      2007-03-30 02:17
    refreshedPanels[i].id = upID + Math.floor(9999 * Math.random());

    var div = document.createElement("div");
    div.id = upID;
    $get("inputContainer").appendChild(div);


    这个强悍, 又一次将客户端脚本给骗了`````
      回复  引用  查看    
  13. #13楼 MK2      2007-03-30 02:33
    这个方案我想到一个不好之处, 就是当同时有几个人在一起发评论, 采用这模式, 只更新了自己的, 却没有显示别人的评论.....
      回复  引用  查看    
  14. #14楼[楼主] Jeffrey Zhao      2007-03-30 02:34
    @MK2
    的确是这样。这样我们可以修改一下一部分逻辑,输出从页面上存在的之后所有的内容。当然,可能也有人会删了添加删了添加等等,不过出现这种情况的概率的确不多。
      回复  引用  查看    
  15. #15楼[楼主] Jeffrey Zhao      2007-03-30 02:35
    @MK2
    当然,要完全解决这种bt情况也不是不可能,只是复杂一些而已。
    例如,可以把目前的评论编号输出至页面,然后在页面上删除那些编号不在的评论。
      回复  引用  查看    
  16. #16楼 MK2      2007-03-30 03:59
    呵呵, 每天都在老赵的"家"发现新"货色"....
      回复  引用  查看    
  17. #17楼 魏晋遗疯      2007-03-30 10:11
    你这儿有创意,我这儿有启发
      回复  引用  查看    
  18. #18楼 Jinhua Ye[未注册用户]2007-03-30 12:30
    回复测试!!!
      回复  引用    
  19. #19楼 Axu[未注册用户]2007-03-30 13:07
    看明白了。有创意的实现方法。
      回复  引用    
  20. #20楼 DataFlow      2007-03-30 14:37
    id改来改去,很累吧……
    不如整个隐藏的update Panel做资源池,接到数据后,再分发……
      回复  引用  查看    
  21. #21楼[楼主] Jeffrey Zhao      2007-03-30 16:30
    @DataFlow
    没有听懂您的意思,您能具体讲一下吗?
      回复  引用  查看    
  22. #22楼 MK2      2007-03-30 16:36
    @DataFlow
    以数据为中心?
      回复  引用  查看    
  23. #23楼 火狐[未注册用户]2007-03-30 17:57
    正在研究中
      回复  引用    
  24. #24楼 kakin[未注册用户]2007-04-06 16:17
    This is Control A.
    <asp:LinkButton ID="LinkButton1" runat="server" OnClick="LinkButton1_Click">Switch To Control B</asp:LinkButton>&nbsp;
    <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="TextBox1"
    ErrorMessage="RequiredFieldValidator"></asp:RequiredFieldValidator>

    假如我加了个textbox必须输才能提交...,那么如果load Control A的时候,那个验证控件就不起作用了...
      回复  引用    
  25. #25楼 kakin[未注册用户]2007-04-06 16:19
    SORRY,发错了,应该发到SwitchPartManager里去的
      回复  引用    
  26. #26楼 mble[未注册用户]2007-04-10 19:35
    如果想让updatePanel中TextBox1获得焦点,该如何做?试了,好像不可以得到
      回复  引用    
  27. #27楼[楼主] Jeffrey Zhao      2007-04-10 20:32
    @mble
    您可以通过ScriptManager的RegisterStartupScript方法来注册异步刷新后执行的脚本。
      回复  引用  查看    
  28. #28楼 coofucoo[未注册用户]2007-05-08 13:27
    @DataFlow, Jeffrey Zhao
    我也赞同DataFlow的说法,其实Jeffrey Zhao这里的用法实际上是将update panel当成了另一个XHR运用了,用它请求特定的服务器资源过来。这里的update panel其实可以用iframe来替换,这就又回到了当初XHR未出现之前,人们实现异步刷新的方法了。Jeffrey Zhao想想是么。
      回复  引用    
  29. #29楼 sun[未注册用户]2007-08-06 11:42
    刚才试了一下那个案例,但是为什么提交过后文本框的内容没有清空啊
      回复  引用    
  30. #30楼 sun[未注册用户]2007-08-06 11:43
    要这样才能清空啊
      回复  引用    
  31. #31楼[楼主] Jeffrey Zhao      2007-08-06 13:34
    @sun
    该怎么清空就怎么清空啊……JavaScript也好,用UpdatePanel也好……
      回复  引用  查看    
  32. #32楼 ww[未注册用户]2007-09-03 20:17
    ww
      回复  引用    
  33. #33楼 Charles2008[未注册用户]2008-11-05 18:08
    不错,updatepanel的灵活运用.
      回复  引用    



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 693271




相关文章:

相关链接: