Hello World
Spiga

UpdatePanel的妙用:Incremental Content

2007-03-29 20:18 by 老赵, 7217 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来包装了内容,它的作用,可以说是为了给每次添加的内容设定一个模版。如下:

<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中出现内容。如下:

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进行辅助。如下:

<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参考。:)

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

33 条回复

  1. 上网助手[未注册用户]
    *.*.*.*
    链接

    上网助手[未注册用户] 2007-03-29 20:35:00

    收藏了。。

  2. Dflying Chen
    *.*.*.*
    链接

    Dflying Chen 2007-03-29 20:38:00

    @上网助手
    广告真多啊……

  3. test[未注册用户]
    *.*.*.*
    链接

    test[未注册用户] 2007-03-29 20:50:00

    test ajax请删除~

  4. 哈密瓜牌牛奶
    *.*.*.*
    链接

    哈密瓜牌牛奶 2007-03-29 21:36:00

    我也来了,学习中:)

  5. 大剑师
    *.*.*.*
    链接

    大剑师 2007-03-29 22:17:00

    哈哈,有创意,有启发

  6. edrp.cn
    *.*.*.*
    链接

    edrp.cn 2007-03-29 22:19:00

    今天没有时间听课,什么时候可以下载?

  7. ※ABeen※
    *.*.*.*
    链接

    ※ABeen※ 2007-03-29 22:30:00

    真好用!不过不明白Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded
    这是什么意思?

  8. 老赵
    admin
    链接

    老赵 2007-03-29 22:32:00

    @edrp.cn
    ppt和Demo已经可以在我的blog上下载到.

  9. 老赵
    admin
    链接

    老赵 2007-03-29 22:33:00

    @※ABeen※
    在客户端相应pageLoaded事件,这时候UpdatePanel已经更新好了。

  10. Cat Chen
    *.*.*.*
    链接

    Cat Chen 2007-03-29 23:26:00

    这东西不错,在某些情境下会非常有用。

  11. Clark Zheng
    *.*.*.*
    链接

    Clark Zheng 2007-03-30 01:40:00

    一直关注!

  12. MK2
    *.*.*.*
    链接

    MK2 2007-03-30 02:17:00

    refreshedPanels[i].id = upID + Math.floor(9999 * Math.random());

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


    这个强悍, 又一次将客户端脚本给骗了`````

  13. MK2
    *.*.*.*
    链接

    MK2 2007-03-30 02:33:00

    这个方案我想到一个不好之处, 就是当同时有几个人在一起发评论, 采用这模式, 只更新了自己的, 却没有显示别人的评论.....

  14. 老赵
    admin
    链接

    老赵 2007-03-30 02:34:00

    @MK2
    的确是这样。这样我们可以修改一下一部分逻辑,输出从页面上存在的之后所有的内容。当然,可能也有人会删了添加删了添加等等,不过出现这种情况的概率的确不多。

  15. 老赵
    admin
    链接

    老赵 2007-03-30 02:35:00

    @MK2
    当然,要完全解决这种bt情况也不是不可能,只是复杂一些而已。
    例如,可以把目前的评论编号输出至页面,然后在页面上删除那些编号不在的评论。

  16. MK2
    *.*.*.*
    链接

    MK2 2007-03-30 03:59:00

    呵呵, 每天都在老赵的"家"发现新"货色"....

  17. 魏晋遗疯
    *.*.*.*
    链接

    魏晋遗疯 2007-03-30 10:11:00

    你这儿有创意,我这儿有启发

  18. Jinhua Ye[未注册用户]
    *.*.*.*
    链接

    Jinhua Ye[未注册用户] 2007-03-30 12:30:00

    回复测试!!!

  19. Axu[未注册用户]
    *.*.*.*
    链接

    Axu[未注册用户] 2007-03-30 13:07:00

    看明白了。有创意的实现方法。

  20. DataFlow
    *.*.*.*
    链接

    DataFlow 2007-03-30 14:37:00

    id改来改去,很累吧……
    不如整个隐藏的update Panel做资源池,接到数据后,再分发……

  21. 老赵
    admin
    链接

    老赵 2007-03-30 16:30:00

    @DataFlow
    没有听懂您的意思,您能具体讲一下吗?

  22. MK2
    *.*.*.*
    链接

    MK2 2007-03-30 16:36:00

    @DataFlow
    以数据为中心?

  23. 火狐[未注册用户]
    *.*.*.*
    链接

    火狐[未注册用户] 2007-03-30 17:57:00

    正在研究中

  24. kakin[未注册用户]
    *.*.*.*
    链接

    kakin[未注册用户] 2007-04-06 16:17:00

    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. kakin[未注册用户]
    *.*.*.*
    链接

    kakin[未注册用户] 2007-04-06 16:19:00

    SORRY,发错了,应该发到SwitchPartManager里去的

  26. mble[未注册用户]
    *.*.*.*
    链接

    mble[未注册用户] 2007-04-10 19:35:00

    如果想让updatePanel中TextBox1获得焦点,该如何做?试了,好像不可以得到

  27. 老赵
    admin
    链接

    老赵 2007-04-10 20:32:00

    @mble
    您可以通过ScriptManager的RegisterStartupScript方法来注册异步刷新后执行的脚本。

  28. coofucoo[未注册用户]
    *.*.*.*
    链接

    coofucoo[未注册用户] 2007-05-08 13:27:00

    @DataFlow, Jeffrey Zhao
    我也赞同DataFlow的说法,其实Jeffrey Zhao这里的用法实际上是将update panel当成了另一个XHR运用了,用它请求特定的服务器资源过来。这里的update panel其实可以用iframe来替换,这就又回到了当初XHR未出现之前,人们实现异步刷新的方法了。Jeffrey Zhao想想是么。

  29. sun[未注册用户]
    *.*.*.*
    链接

    sun[未注册用户] 2007-08-06 11:42:00

    刚才试了一下那个案例,但是为什么提交过后文本框的内容没有清空啊

  30. sun[未注册用户]
    *.*.*.*
    链接

    sun[未注册用户] 2007-08-06 11:43:00

    要这样才能清空啊

  31. 老赵
    admin
    链接

    老赵 2007-08-06 13:34:00

    @sun
    该怎么清空就怎么清空啊……JavaScript也好,用UpdatePanel也好……

  32. ww[未注册用户]
    *.*.*.*
    链接

    ww[未注册用户] 2007-09-03 20:17:00

    ww

  33. Charles2008[未注册用户]
    *.*.*.*
    链接

    Charles2008[未注册用户] 2008-11-05 18:08:00

    不错,updatepanel的灵活运用.

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我