Spiga

技巧:使用User Control做HTML生成

2007-12-30 23:06 by Jeffrey Zhao, 19154 visits, 收藏, 编辑

User Control大家肯定不会陌生,在使用ASP.NET的过程中,除了aspx页面,最常见的就莫过于ascx了。ascx是一个有独立逻辑的组件,提供了强大的复用特性,合理使用,能够大大提高开发效率。通过User Control直接生成HTML内容其实已经是一个比较常用的技巧了(尤其在AJAX时代),不过网络上这方面的内容比较少,很多人还是在苦苦地拼接字符串,因此在这里我通过一个实例简单介绍一下这个技巧。

对一个对象(文章,图片,音乐,etc.)进行评论是应用中最常见的功能之一。首先,我们定义一个Comment类,以及其中会用到的“获取”方法:

public partial class Comment
{
    public DateTime CreateTime { get; set; }
 
    public string Content { get; set; }
}
 
public partial class Comment
{
    private static List<Comment> s_comments = new List<Comment>
    {
        new Comment
        {
            CreateTime = DateTime.Parse("2007-1-1"),
            Content = "今天天气不错"
        },
        new Comment
        {
            CreateTime = DateTime.Parse("2007-1-2"),
            Content = "挺风和日丽的"
        },
        new Comment
        {
            CreateTime = DateTime.Parse("2007-1-3"),
            Content = "我们下午没有课"
        },
        new Comment
        {
            CreateTime = DateTime.Parse("2007-1-1"),
            Content = "这的确挺爽的"
        }
    };
 
    public static List<Comment> GetComments(int pageSize, int pageIndex, out int totalCount)
    {
        totalCount = s_comments.Count;
 
        List<Comment> comments = new List<Comment>(pageSize);
 
        for (int i = pageSize * (pageIndex - 1);
            i < pageSize * pageIndex && i < s_comments.Count; i++)
        {
            comments.Add(s_comments[i]);
        }
 
        return comments;
    }
}

为了显示一个评论列表,我们可以使用一个用户控件(ItemComments.aspx)来封装。自然,分页也是必不可少的:

<asp:Repeater runat="server" ID="rptComments">
    <ItemTemplate>
        时间:<%# (Container.DataItem as Comment).CreateTime.ToString() %><br />
        内容:<%# (Container.DataItem as Comment).Content %> 
    </ItemTemplate>
    <SeparatorTemplate>
        <hr />
    </SeparatorTemplate>
    <FooterTemplate>
        <hr />
    </FooterTemplate>
</asp:Repeater>
 
<% if (this.PageIndex > 1)
   { %>
        <a href="/ViewItem.aspx?page=<%= this.PageIndex - 1 %>" title="上一页">上一页</a>&nbsp;
<% } %>

<% if (this.PageIndex * this.PageSize < this.TotalCount)
   { %>
        <a href="/ViewItem.aspx?page=<%= this.PageIndex + 1 %>" title="上一页">下一页</a>
<% } %>

还有:

public partial class ItemComments : System.Web.UI.UserControl
{
    protected override void OnPreRender(EventArgs e)
    {
        base.OnPreRender(e);
 
        this.rptComments.DataSource = Comment.GetComments(this.PageSize,
            this.PageIndex, out this.m_totalCount);
        this.DataBind();
    }
 
    public int PageIndex { get; set; }
 
    public int PageSize { get; set; }
 
    private int m_totalCount;
    public int TotalCount
    {
        get
        {
            return this.m_totalCount;
        }
    }
} 

然后再页面(ViewItem.aspx)中使用这个组件:

<div id="comments"><demo:ItemComments ID="itemComments" runat="server" /></div>

以及:

public partial class ViewItem : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        this.itemComments.PageIndex = this.PageIndex;
    }
 
    protected int PageIndex
    {
        get
        {
            int result = 0;
            Int32.TryParse(this.Request.QueryString["page"], out result);
 
            return result > 0 ? result : 1;
        }
    }
} 

打开ViewItem.aspx之后效果如下:

时间:2007/1/1 0:00:00 内容:今天天气不错
时间:2007/1/2 0:00:00 内容:挺风和日丽的
时间:2007/1/3 0:00:00 内容:我们下午没有课
下一页

这张页面的功能非常简单,那就是察看评论。当前评论的页码会使用QueryString的page项进行指定,然后在ViewItem.aspx里获取到并且设置ItemComments.ascx控件的属性。ItemComments控件会根据自身属性来获取数据,进行绑定,至于显示内容,全都定义在ascx中了。由于需要分页功能,这个评论控件中还包含了上一页和下一页的链接,他们链接的目标很简单,就是ViewItem.aspx页,并且加上页码的Query String而已。

功能是完成了,不过用着用着忽然觉得不妥,为什么呢?因为我们在翻页,或者用户发布评论的时候,整张页面都刷新了。这可不好,要知道可能ViewItem页中还有其他几个显示部分,它们可是不变的。而且如果其他几个部分也需要分页,那么可能就需要保留页面上每一部分的当前页码,这样开发的复杂性还是比较高的。

那么我们不如用AJAX吧。无论是用户察看评论时进行翻页还是发表评论,都不会对页面上的其他内容造成影响。要开发这个功能,自然需要服务器端的支持,那么该怎么做呢?一般我们总是有两种选择:

  1. 服务器端返回JSON数据,在客户端操作DOM进行呈现。
  2. 服务器端直接返回HTML内容,然后在客户端设置容器(例如上面id为comments的div)。

不过无论采用哪种做法,“呈现”的逻辑一般总是另写一遍(第一次的呈现逻辑写在了ItemComments.ascx中)。如果使用第1种做法,那么呈现逻辑就需要在客户端通过操作DOM进行呈现;如果使用第2种做法,那么就要在服务器端进行字符串拼接。无论哪种做法都违背了DRY原则,当ItemComments.ascx里的呈现方式修改时,另一处也要跟着修改。而且无论是操作DOM元素还是拼接字符串维护起来都比较麻烦,开发效率自然也就不高了。

如果我们能够直接从ItemComments控件获得HTML内容该多好啊——那么我们就这么做吧。请看如下代码(GetComments.ashx):

public class GetComments : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/plain";
 
        ViewManager<ItemComments> viewManager = new ViewManager<ItemComments>();
        ItemComments control = viewManager.LoadViewControl("~/ItemComments.ascx");
 
        control.PageIndex = Int32.Parse(context.Request.QueryString["page"]);
        control.PageSize = 3;
 
        context.Response.Write(viewManager.RenderView(control));
    }
 
    public bool IsReusable { ... }
} 

很简单的代码,不是吗?创建对象,设置属性,然后通过Response.Write输出而已。实在没什么大不了的——不过关键就在于ViewManager类,我们来看一下它是怎么实现的:

public class ViewManager<T> where T : UserControl
{
    private Page m_pageHolder;
 
    public T LoadViewControl(string path)
    {
        this.m_pageHolder = new Page();
        return (T)this.m_pageHolder.LoadControl(path);
    }
 
    public string RenderView(T control)
    {
        StringWriter output = new StringWriter();
 
        this.m_pageHolder.Controls.Add(control);
        HttpContext.Current.Server.Execute(this.m_pageHolder, output, false);
 
        return output.ToString();
    }
}

ViewManager中只有两个方法:LoadViewControl和RenderView。LoadViewControl方法的作用是创建一个Control实例并返回,RenderView方法的作用则就是生成HTML了。这个实现方式的技巧在于使用了一个新建的Page对象作为生成控件的“容器”,而最后其实我们是将Page对象的整个生命周期运行一遍,并且将结果输出。由于这个空的Page对象不会产生任何其他代码,因此我们得到的,就是用户控件生成的代码了。

不过要实现这个AJAX效果,还需要做两件事情。

第一,就是简单修改一下ItemComments控件中的翻页链接,让它被点击时调用一个JavaScript函数。例如“上一页”的代码就会变成:

<a href="/ViewItem.aspx?page=<%= this.PageIndex - 1 %>" title="上一页"
    onclick="return getComments(<%= this.PageIndex - 1 %>);">上一页</a>

第二,就是实现getComments这个客户端方法。在这里我使用了prototype框架,好处就是能够用相当简洁的代码来做到替换HTML的AJAX效果:

<script type="text/javascript" language="javascript">
    function getComments(pageIndex)
    {
        new Ajax.Updater(
            "comments",
            "/GetComments.ashx?page=" + pageIndex + "&t=" + new Date(),
            { method: "get" }); 
        
        return false; // IE only
    }
</script>

大功告成。

其实就像之前所说的那样,使用UserControl进行HTML代码生成是一个十分常用的技巧。尤其在AJAX应用越来越普及的情况下,合理使用上面提到的方式可以方便的为我们的应用添加AJAX效果。而且很多情况下,我们即使不需要在页面上显示内容,也可以将内容使用UserControl进行编辑。因为编写UserControl比拼接字符串的方式无论是在开发效率上还是可维护性上都高出许多。由于这个方式其实使用了WebForms这个久经考验的模型,因此在执行效率方面也是相当高的。此外,就刚才的例子来说,使用UserCotrol进行HTML生成还有其他好处:

  1. 页面呈现逻辑只实现了一次,提高了可维护性。
  2. 不会影响页面的SEO,因为在客户端<a />的href还是有效的。

事实上,WebForms是一个非常强大的模型,所以ASP.NET MVC的View也使用了WebForms的引擎。通过上面这个例子,我们其实还可以做到其他很多东西——例如用UserControl来生成XML数据,因为UserControl本身不会带来任何额外的内容。

标签: HTML
Add your comment

250 条回复

    评论共3页: 上一页 1 2 3 
  1. #150楼[楼主] Jeffrey Zhao      2008-04-16 11:00
    @mclkyo
    别的项目?命名空间正确了没?
     回复 引用 查看   
  2. #151楼 mclkyo[未注册用户]2008-04-16 12:03
    项目,命名空间都没问题
    我发现在aspx.cs文件中要识别ItemComments这个类就必须在Aspx页面加上<%@ Register src=''%>头
    但在我如果要在AppCode的类里面引用ItemComments这个类就不知道怎么做了
     回复 引用   
  3. #152楼[楼主] Jeffrey Zhao      2008-04-16 20:47
    @mclkyo
    App_Code没有引用WebSite里的项目,自然无法使用。
    这段代码不能写在App_Code里。
     回复 引用 查看   
  4. #153楼 希望回答[未注册用户]2008-04-27 11:09
    麻烦问下 我的用户控件和ashx文件不是放在同一目录下
    ControlManager<HeadControl> viewManager = new ControlManager<HeadControl>();
    HeadControl control = viewManager.LoadViewControl("Controls/HeadControl.ascx");
    无法编译通过,用命名控件组织也不可以。想问下怎么解决。
    谢谢。。。。
     回复 引用   
  5. #154楼[楼主] Jeffrey Zhao      2008-04-27 14:37
    @希望回答
    放在额外的Class Library里。
     回复 引用 查看   
  6. #155楼 zys1202[未注册用户]2008-05-07 13:44
    ControlManager<HeadControl> viewManager = new ControlManager<HeadControl>();
    HeadControl control = viewManager.LoadViewControl("Controls/HeadControl.ascx");
    无法编译通过,用命名控件组织也不可以。想问下怎么解决。
    找不到命名空间或类型名称 HeadControl
     回复 引用   
  7. #156楼[楼主] Jeffrey Zhao      2008-05-07 15:03
    HeadControl定义在什么地方?
     回复 引用 查看   
  8. #157楼 五味果      2008-05-23 15:38
    老赵,你能把这个例子能提供下载或者给我邮箱发一份吗
    我的邮箱:taibaizhou@163.com
    我在调试你的例子时候遇到,
    ViewManager<ItemComments> viewManager = new ViewManager<ItemComments>();
    ItemComments control = viewManager.LoadViewControl("ItemComments.ascx");
    ItemComments类不能识别出来。
    麻烦你了!
     回复 引用 查看   
  9. #158楼 jim.chb[未注册用户]2008-06-01 23:25
    老赵,首先谢谢您的无私贡献哦!我在调试的时候遇到这个问题:执行到
    HttpContext.Current.Server.Execute(this.m_pageHolder, output, false);

    这里的时候,报错:

    Error executing child request for handler 'System.Web.UI.Page'.

    老赵能给点思路么,多谢!
     回复 引用   
  10. #159楼 jim.chb[未注册用户]2008-06-01 23:28
    其中的 output变量已经取出usercontrol的html字符串了 ,但就不能return呢?
     回复 引用   
  11. #160楼[楼主] Jeffrey Zhao      2008-06-02 00:10
    @jim.chb
    执行时出现异常?我的例子你能运行吗?
     回复 引用 查看   
  12. #161楼 r[未注册用户]2008-06-04 17:23
    看了,凑个数
     回复 引用   
  13. #162楼 qinii[未注册用户]2008-06-04 19:53
    晕了。总算找到了。。。。。。。。。。。
    还是去年写的,郁闷~~~~一直认为可以这样实现,只怪本人对asp.net还深入学习,没办法自己实现。。。

     回复 引用   
  14. #163楼 iceflying[未注册用户]2008-06-05 15:29
    老赵 我无法在ashx 中访问你的ItemComments 类
    是不是还要using什么东西,怎么调用外部的class lib啊 谢谢阿

    using System;
    using System.Web;
    using System.Web.UI;

    public class CallUC : IHttpHandler {

    public void ProcessRequest (HttpContext context) {
    context.Response.ContentType = "text/plain";

    ViewManager<ItemComments> viewManager = new ViewManager<ItemComments>();
    UserControl control = viewManager.LoadViewControl("~/testControl.ascx");
    control.
    control.PageIndex = Int32.Parse(context.Request.QueryString["page"]);
    control.PageSize = 3;

    context.Response.Write(viewManager.RenderView(control));
    }

    public bool IsReusable {
    get {
    return false;
    }
    }

    }
     回复 引用   
  15. #164楼 beafhorse[未注册用户]2008-06-10 12:42
    能不能把这个东西和web service结合起来?
     回复 引用   
  16. #165楼 beafhorse[未注册用户]2008-06-11 00:12
    还有个问题.如果UserControl里面也有javascript代码,这些代码会正常实现么?我测试了一下,似乎不行.
     回复 引用   
  17. #166楼[楼主] Jeffrey Zhao      2008-06-11 00:23
    --引用--------------------------------------------------
    beafhorse: 还有个问题.如果UserControl里面也有javascript代码,这些代码会正常实现么?我测试了一下,似乎不行.
    --------------------------------------------------------
    肯定不行的
     回复 引用 查看   
  18. #167楼 beafhorse[未注册用户]2008-06-11 14:33
    --引用--------------------------------------------------
    Jeffrey Zhao: --引用--------------------------------------------------
    beafhorse: 还有个问题.如果UserControl里面也有javascript代码,这些代码会正常实现么?我测试了一下,似乎不行.
    --------------------------------------------------------
    肯定不行的
    --------------------------------------------------------
    那如果UserControl中必须放入一些js代码的话,本文讲的这个方法是不是就不适用了?
     回复 引用   
  19. #168楼[楼主] Jeffrey Zhao      2008-06-11 16:08
    @beafhorse
    在这个基础上做点补充阿。
     回复 引用 查看   
  20. #169楼 beafhorse[未注册用户]2008-06-11 17:32
    --引用--------------------------------------------------
    Jeffrey Zhao: @beafhorse
    在这个基础上做点补充阿。
    --------------------------------------------------------
    如何做,给点提示吧.
     回复 引用   
  21. #170楼 Eve[未注册用户]2008-06-13 04:50
    --引用--------------------------------------------------
    Jeffrey Zhao: --引用--------------------------------------------------
    beafhorse: 还有个问题.如果UserControl里面也有javascript代码,这些代码会正常实现么?我测试了一下,似乎不行.
    --------------------------------------------------------
    肯定不行的
    --------------------------------------------------------
    “UserControl里面也有javascript代码,这些代码会正常实现么?”指的是什么?
    又为什么肯定不行?
    我试了一下似乎都能运行啊?
     回复 引用   
  22. #171楼[楼主] Jeffrey Zhao      2008-06-13 10:34
    --引用--------------------------------------------------
    “UserControl里面也有javascript代码,这些代码会正常实现么?”指的是什么?
    又为什么肯定不行?
    我试了一下似乎都能运行啊?
    --------------------------------------------------------
    不可能的,你是在用我这种设置innerHTML的方式更新吗?
     回复 引用 查看   
  23. #172楼 beafhorse[未注册用户]2008-06-13 10:40
    --引用--------------------------------------------------
    Jeffrey Zhao: --引用--------------------------------------------------
    “UserControl里面也有javascript代码,这些代码会正常实现么?”指的是什么?
    又为什么肯定不行?
    我试了一下似乎都能运行啊?
    --------------------------------------------------------
    不可能的,你是在用我这种设置innerHTML的方式更新吗?
    --------------------------------------------------------
    那以何种方式更新可以让UserControl中的javascript代码正常执行呢?困惑了两天了.
     回复 引用   
  24. #173楼 beafhorse[未注册用户]2008-06-13 11:23
    昨天看老赵的asp.net ajax教程,有了思路.
    把usercontrol里面的script代码提取出来.放在页面的head中解析.

    这样是否可行?还有更好的办法没?
     回复 引用   
  25. #174楼 beafhorse[未注册用户]2008-06-13 11:31
    如果只考虑ie,加个defer就可以了.firefox有没有类似的标签?

     回复 引用   
  26. #175楼 Sam Lin      2008-06-13 18:51
    看了颇久代码,在把代码copy在vs2005 C#中,怎么老是出错~~~
    原来是C#3.0的新特性~啊,看来要开始学习3.0了
    好象从2.0到3.0新加的东西,大多是从javascript中拿过来的
    比如匿名、委托、还有刚才的创建对象……

    另外想问一下:
    通过这种url重写,把所有的aspx都重写成html
    实际上只是一种伪地址,真正访问的时候也还是aspx页面,这种做能提高搜索引擎收录率吗?还有应该不会对网站的性能有所提高吧?
     回复 引用 查看   
  27. #176楼 Eve[未注册用户]2008-06-13 19:39
    @ Jeffrey Zhao
    --引用--------------------------------------------------
    不可能的,你是在用我这种设置innerHTML的方式更新吗?
    --------------------------------------------------------
    我用的jQuery的html(),不知道它是不是innerHTML方式。

    我的代码:
    <script src="js/jquery-1.2.3.pack.js" type="text/javascript"></script>
    <script type="text/javascript">
    function getComments(pageIndex)
    {
    $.ajax({
    url: "GetComments.ashx?page=" + pageIndex + "&t=" + new Date(),
    type: "get",
    success: function(msg) {
    $("#comments").html(msg);

    }
    });

    }
    </script>
    这段代码直接放在UserControl里也可以运行的。
     回复 引用   
  28. #177楼[楼主] Jeffrey Zhao      2008-06-14 01:49
    --引用--------------------------------------------------
    beafhorse: 昨天看老赵的asp.net ajax教程,有了思路.
    把usercontrol里面的script代码提取出来.放在页面的head中解析.

    这样是否可行?还有更好的办法没?
    --------------------------------------------------------
    则就是标准的做法了吧,当然不一定要放在head里,eval一下就行。
     回复 引用 查看   
  29. #178楼[楼主] Jeffrey Zhao      2008-06-14 01:49
    --引用--------------------------------------------------
    beafhorse: 如果只考虑ie,加个defer就可以了.firefox有没有类似的标签?
    --------------------------------------------------------
    defer是标准,可惜firefox不支持
     回复 引用 查看   
  30. #179楼[楼主] Jeffrey Zhao      2008-06-14 01:50
    --引用--------------------------------------------------
    Sam Lin: 看了颇久代码,在把代码copy在vs2005 C#中,怎么老是出错~~~
    原来是C#3.0的新特性~啊,看来要开始学习3.0了
    好象从2.0到3.0新加的东西,大多是从javascript中拿过来的
    比如匿名、委托、还有刚才的创建对象……

    另外想问一下:
    通过这种url重写,把所有的aspx都重写成html
    实际上只是一种伪地址,真正访问的时候也还是aspx页面,这种做能提高搜索引擎收录率吗?还有应该不会对网站的性能有所提高吧?
    --------------------------------------------------------
    其实新特性是参考动态语言的,不一定是JavaScript。
    至于url重写,把aspx写成html不会造成多大差别,也不会提高性能。
     回复 引用 查看   
  31. #180楼[楼主] Jeffrey Zhao      2008-06-14 01:51
    --引用--------------------------------------------------
    Eve: @ Jeffrey Zhao
    我用的jQuery的html(),不知道它是不是innerHTML方式。

    我的代码:
    &lt;script src=&quot;js/jquery-1.2.3.pack.js&quot; type=&quot;text/javascript&quot;&gt;&lt;/script&gt;
    &lt;script type=&quot;text/javascript&quot;&gt;
    function getComments(pageIndex)
    {
    $.ajax({
    url: &quot;GetComments.ashx?page=&quot; + pageIndex + &quot;&amp;t=&quot; + new Date(),
    type: &quot;get&quot;,
    success: function(msg) {
    $(&quot;#comments&quot;).html(msg);

    }
    });

    }
    &lt;/script&gt;
    这段代码直接放在UserControl里也可以运行的。
    --------------------------------------------------------
    jquery自动提取script代码后eval,并设置innerHTML
     回复 引用 查看   
  32. #181楼 brightwang      2008-06-17 22:39
    我的做法是专门建个页面,然后把要输出的内容如(repeater)放在页面上,把body、html等无关的元素都去掉,然后代码绑定就可以直接输出到客户端,不知道你认为这中方式如何。
     回复 引用 查看   
  33. #182楼[楼主] Jeffrey Zhao      2008-06-17 23:14
    --引用--------------------------------------------------
    brightwang: 我的做法是专门建个页面,然后把要输出的内容如(repeater)放在页面上,把body、html等无关的元素都去掉,然后代码绑定就可以直接输出到客户端,不知道你认为这中方式如何。
    --------------------------------------------------------
    效果一样
     回复 引用 查看   
  34. #183楼 beafhorse[未注册用户]2008-06-24 15:07
    --引用--------------------------------------------------
    希望回答: 麻烦问下 我的用户控件和ashx文件不是放在同一目录下
    ControlManager&lt;HeadControl&gt; viewManager = new ControlManager&lt;HeadControl&gt;();
    HeadControl control = viewManager.LoadViewControl(&quot;Controls/HeadControl.ascx&quot;);
    无法编译通过,用命名控件组织也不可以。想问下怎么解决。
    谢谢。。。。
    --------------------------------------------------------
    同样的问题.
    如何才能让ashx找到usercontrol的那个类?
    您上面提到了调用外部类.如何调用?给点提示吧.
    如果都改成ViewManager<UserControl> viewManager = new ViewManager<UserControl>(); 虽然可以编译通过,但是使用起来很不方便.无法传值给ItemComment .
     回复 引用   
  35. #184楼 tsgtx[未注册用户]2008-07-03 08:03
    为什么不能用rendercontrol将用户控件转换成html串呢?
    别人已经做好的用户控件,我要怎么做才能把它转换成html串呢?
     回复 引用   
  36. #185楼 tsgtx[未注册用户]2008-07-03 08:04
    为什么不能用rendercontrol将TabContainer及其它包含的控制转换成html串呢?
     回复 引用   
  37. #186楼 willieQ      2008-07-07 16:08
    website时,怎么定位用户控件类?
    ----对应代码--------
    ViewManager<ItemComments> viewManager = new ViewManager<ItemComments>();
    ======================

    我的用户控件类是
    uc_WebUserControl
    路径是/uc/

    我直接写成----
    ViewManager<uc_WebUserControl> viewManager = new ViewManager<uc_WebUserControl>();

    不行
     回复 引用 查看   
  38. #187楼 代码乱了      2008-07-13 23:05
    这个方法很不错,很实用
     回复 引用 查看   
  39. #188楼 singleblue      2008-07-14 08:53
    这边的m_pageHolder,如果用Control来代替,效率是否能高一点点?
     回复 引用 查看   
  40. #189楼[楼主] Jeffrey Zhao      2008-07-14 09:38
    @singleblue
    没法用Control的。
     回复 引用 查看   
  41. #190楼 matta[未注册用户]2008-07-14 16:44
    其实比较贴切的说,是指你的
    HttpContext.Current.Server.Execute(this.m_pageHolder, output, false);

    其实不用这样的开销.
     回复 引用   
  42. #191楼[楼主] Jeffrey Zhao      2008-07-14 16:53
    @matta
    没看出有什么问题来
     回复 引用 查看   
  43. #192楼 matta[未注册用户]2008-07-14 17:06
    一定要这样才行吗,没有更优的了?
    HttpContext.Current.Server.Execute(this.m_pageHolder, output, false);
     回复 引用   
  44. #193楼[楼主] Jeffrey Zhao      2008-07-14 20:25
    @matta
    不说优不优,没有觉得这个东西哪里差了阿,呵呵。
     回复 引用 查看   
  45. #194楼 clefoo      2008-07-15 01:02
    protected void Page_Load(object sender, EventArgs e)
    {
    StringWriter output = new StringWriter();
    top s=(top)this.LoadControl("top.ascx");
    this.Controls.Add(s);
    HttpContext.Current.Server.Execute(this, output, false);
    Response.Write(output.ToString());
    }

    HttpContext.Current.Server.Execute(this, output, false);
    这句是不是可以省略不要了(top是UserControl)
     回复 引用 查看   
  46. #195楼[楼主] Jeffrey Zhao      2008-07-15 01:09
    @clefoo
    没看懂,你这个东西是做什么用的?
     回复 引用 查看   
  47. #196楼 clefoo      2008-07-15 08:59
    @Jeffrey Zhao
    就是你的那个事例改了下。

    userControl生成html
     回复 引用 查看   
  48. #197楼[楼主] Jeffrey Zhao      2008-07-15 11:51
    @clefoo
    能运行么?
     回复 引用 查看   
  49. #198楼 miao~      2008-07-15 12:06
    有个问题,为什么我做的时候 点下一页的时候 如果
    <a href="/ViewItem.aspx?page=<%= this.PageIndex - 1 %>" title="上一页"
    onclick="return getComments(<%= this.PageIndex - 1 %>);">上一页</a>

    就不会执行 onclick 而是直接 跳转到 ViewItem.aspx?page=2

    而要是把 href="javascript:void(0)" 就可以执行 onclick 了....

    prototype 不熟..用 Jquery 改了下

    function getComments(pageIndex)
    {
    $.get("GetComments.ashx",{page:pageIndex,t:new Date()},function(data){$("#comments").html(data)});
    }

    不知道其他的朋友有没有类似的问题?
     回复 引用 查看   
  50. #199楼 clefoo      2008-07-15 12:49
    @miao~
    在href是要加上void(0);

    @Jeffrey Zhao
    可以运行
     回复 引用 查看   
  51. #200楼 miao~      2008-07-15 13:10
    @clefoo
    但是老赵那个href 没有加 void(0) 啊....
    怎么能让它只执行 onClick 而保留 href 里的并不跳转呢?
     回复 引用 查看   
  52. #201楼[楼主] Jeffrey Zhao      2008-07-15 13:13
    @miao~
    看我的js是怎么写的
     回复 引用 查看   
  53. #202楼 miao~      2008-07-15 13:20
    @Jeffrey Zhao
    非常感谢,少加了
    return false; // IE only


     回复 引用 查看   
  54. #203楼 陈招展      2008-07-17 00:33
    这么多评论啊…
     回复 引用 查看   
  55. #204楼 Fanty      2008-07-21 12:57
    ViewManager里面事实上是存在有一个BUG.如果控件存在着LinkButton,GridView等的必须依赖Form存在的控件,就会出现报错现象!
    我的解决方法是
    /// <summary>
    /// 解释控件HTML
    /// </summary>
    /// <param name="control"></param>
    /// <returns></returns>
    public string RenderView(T control)
    {
    try
    {
    HtmlForm _form = new HtmlForm();
    _form.ID = "form1";
    _form.EnableViewState = false;
    _pageHolder.Controls.Add(_form);
    HtmlGenericControl _ulControl = new HtmlGenericControl("UL");
    _form.Controls.Add(_ulControl);
    _ulControl.Controls.Add(control);
    System.IO.StringWriter output = new System.IO.StringWriter();
    System.Web.HttpContext.Current.Server.Execute(_pageHolder, output, false);
    System.Xml.XmlDocument xmlDoc = new System.Xml.XmlDocument();
    xmlDoc.LoadXml(output.ToString());
    _pageHolder.Controls.Clear();
    output.Close();
    _form.Dispose();

    return xmlDoc.DocumentElement.SelectNodes("UL")[0].InnerXml;
    }
    catch { return string.Empty; }
    }
    至于为什么要用XmlDocument读取指定的HTML模板,就是要去除生成出来多余的HTML代码
     回复 引用 查看   
  56. #205楼[楼主] Jeffrey Zhao      2008-07-21 14:01
    @Fanty
    不是bug,上面的评论就讲过解决方法,你这个做法太复杂了。
     回复 引用 查看   
  57. #206楼 Fanty      2008-07-21 19:28
    @Jeffrey Zhao
    对不起,我没有看见上面的哪有更好的解决方案!
    不过您当时的这种做法给了我很大的构想!令我可以想到2008 MVC下去进行该模式的设计!并且省下要建立ashx的麻烦
     回复 引用 查看   
  58. #207楼[楼主] Jeffrey Zhao      2008-07-21 21:46
    @Fanty
    不直接使用Page,而是使用Page的自定义子类,把VerifyXxxx方法给override掉即可。
     回复 引用 查看   
  59. #208楼 淡淡的[未注册用户]2008-08-28 15:04
    程序发出来了,楼主,福利大家
     回复 引用   
  60. #209楼 淡淡的[未注册用户]2008-08-28 15:05
    楼主多做视频呀。哇哇
     回复 引用   
  61. #210楼 tc11o8      2008-09-11 18:05
    老赵,你能把这个例子能提供下载或者给我邮箱发一份吗
    我的邮箱:zmj119@hotmail.com
     回复 引用 查看   
  62. #211楼 窜天下[未注册用户]2008-09-15 09:40
    这段代码是什么意思:


    List<Comment> comments = new List<Comment>(pageSize);

    for (int i = pageSize * (pageIndex - 1);
    i < pageSize * pageIndex && i < s_comments.Count; i++)


    List<Comment> comments = new List<Comment>(pageSize);这句代码的pageSize传进去干嘛,给谁啊,没属性也没字段的。
    谢谢zhao,麻烦解释一下,我运行不了

     回复 引用   
  63. #212楼 窜天下[未注册用户]2008-09-15 09:41
    笔误多了一点啊,呵呵
     回复 引用   
  64. #213楼 窜天下[未注册用户]2008-09-15 09:47
    你能给我也发一份吧,我的邮箱是levinyang@gmail.com,我研究研究.
     回复 引用   
  65. #214楼 tc11o8      2008-09-18 11:54
    我觉得 用usercontrol 来做html生成 有一些问题
    这样会带来臃于数据的返回 控件中的一些无用的脚本 标签等也都被重复返回到了客户端 这样的做法实现类似于updatepanel 与服务器交互中 数据的传输量丝毫没有减少 还是传统的把整个页面返回到客户端 对网络带宽就得有一定要求了
    我觉得web程序还是最好把有用的数据来进行传输 将数据进行定义成某一格式 比如json,xml等 这样就能带来web服务器数据的复用 虽然开发上可能会复杂一些 但这样实现了松耦合 可以多人开发这个程序 一个做页面处理 一个做服务端开发 彼此可以独立的进行开发 只要他们定义好数据交互的格式就行
     回复 引用 查看   
  66. #215楼[楼主] Jeffrey Zhao      2008-09-21 13:18
    @tc11o8
    文章说了这种做法有利有弊的
     回复 引用 查看   
  67. #216楼 tc11o8      2008-09-21 15:00
    谢谢 楼主 我也常看你的视频教程 :)
     回复 引用 查看   
  68. #217楼 gjj[未注册用户]2008-10-30 15:16
    ViewManager<ItemComments> viewManager = new ViewManager<ItemComments>();
    itemComments
    ItemComments是不能引用出来的
     回复 引用   
  69. #218楼 小涂[未注册用户]2008-10-31 12:57
    博主 你好 看了你的文章很感兴趣,
    但我技术比较差 看的不是很明白
    我想问一下 这个用户控件做HTML生成有没有例子,能不能发个给我研究一下,我现在用到了这个 很想了解 学会 谢谢了 有的话发个到我邮箱给我 真的感谢
     回复 引用   
  70. #219楼 nick_temp[未注册用户]2008-12-27 02:28
    //老赵:如果加载的用户控件用了缓存的话,是存在问题的。
    //我在网上找了一些资料,但还是没有解决问题
    //((PartialCachingControl)crl).CachedControl这上值一直是null.
    //所以没有办法实例化控件。
    链接地址:http://www.aspnetpro.com/NewsletterArticle/2003/08/asp200308pj_l/asp200308pj_l.asp
    他的例子是在一个页面里加载的用户控件,而你的是实例化一个页面容器来
    加载用户控件的。

    //下面是我的代码。
    public class ViewManager<T> where T : UserControl
    {
    private PageContainer m_pageHolder;

    public T LoadViewControl(string path)
    {
    this.m_pageHolder = new PageContainer();
    Control crl = this.m_pageHolder.LoadControl(path);
    Control currentCrl = null;
    if (crl is T)
    {
    currentCrl = crl;
    }
    else if (crl is PartialCachingControl && ((PartialCachingControl)crl).CachedControl != null)
    {
    currentCrl = ((PartialCachingControl)crl).CachedControl;
    }
    return (T)currentCrl;
    }

    public string RenderView(T control)
    {
    StringWriter output = new StringWriter();

    this.m_pageHolder.Controls.Add(control);
    HttpContext.Current.Server.Execute(this.m_pageHolder, output, false);

    return output.ToString();
    }
    }
     回复 引用   
  71. #220楼 fengO[未注册用户]2009-01-16 10:13
    我把代码放在了Web服务的后置代码文件中,再现了下面的错误,

    编译错误
    说明: 在编译向该请求提供服务所需资源的过程中出现错误。请检查下列特定错误详细信息并适当地修改源代码。

    编译器错误信息: CS0246: The type or namespace name 'PagingData' could not be found (are you missing a using directive or an assembly reference?)

    如果放在一个.asmx文件中就可以,不知道其它人有没有这样的事情发生过,有没有什么解决方法?
     回复 引用   
  72. #221楼 renshivs[未注册用户]2009-02-06 17:03
    --引用--------------------------------------------------
    gjj: ViewManager&lt;ItemComments&gt; viewManager = new ViewManager&lt;ItemComments&gt;();
    itemComments
    ItemComments是不能引用出来的

    --------------------------------------------------------
    我也无法引用,不知道赵老师能不能解答下?
     回复 引用   
  73. #222楼 zachary[未注册用户]2009-03-10 13:49
    你好,老赵
    看了你的代码自己也在项目中想用一把
    结果却是在HttpHandler类里动态加载ascx时候报编译错:找不到类型或命名空间名称
    我用的是2005开发的,能不能将你的代码发我一份,让我研究学习一下呢
    我的邮箱地址:zachary.woo@163.com
     回复 引用   
  74. #223楼 彭白洋      2009-03-30 18:35
    楼主,您好!问个低级的问题啊:嘿嘿

    这事您的代码:

    public class ViewManager<T> where T : UserControl
    {
    private Page m_pageHolder;

    public T LoadViewControl(string path)
    {
    this.m_pageHolder = new Page();
    return (T)this.m_pageHolder.LoadControl(path);
    }

    public string RenderView(T control)
    {
    StringWriter output = new StringWriter();

    this.m_pageHolder.Controls.Add(control);
    HttpContext.Current.Server.Execute(this.m_pageHolder, output, false);

    return output.ToString();
    }
    }


    我想问的是,为什么不再ashx也中直接用
    context.Server.Execute("~/ItemComments.ascx",output,false);呢?
    为什么要先放到容器中执行一下呢?

    StringWriter output = new StringWriter();
    context.Server.Execute("~/ItemComments.ascx", output, false);
    context.Response.Write(output);

    这个地方小弟很迷惑 谢谢楼主解答……
     回复 引用 查看   
  75. #224楼[楼主] Jeffrey Zhao      2009-03-30 20:10
    @彭白洋
    1、真可以吗?
    2、参数如何设置?
     回复 引用 查看   
  76. #225楼 彭白洋      2009-03-31 13:03
    我用的 vs2008 .net3.5
    是可以的
    不用定义一个容器 重新加载一下控件的


    就是这样:
    StringWriter output = new StringWriter();
    context.Server.Execute("~/ItemComments.ascx", output, false);
    context.Response.Write(output);


    Execute有一个这样的 重载方法的
     回复 引用 查看   
  77. #226楼[楼主] Jeffrey Zhao      2009-03-31 15:20
    @彭白洋
    参数可以设置吗?
     回复 引用 查看   
  78. #227楼 邢少      2009-09-14 10:01
    不错.··!一个心得思路。非常好.哈哈.虽然大概是我想的生成过程。但是在具体实现上很简单,但是很精简。学习!
     回复 引用 查看   
  79. #228楼 蛙蛙王子      2009-09-24 13:16
    才看到,不错,再看看老赵其它的老贴子。
     回复 引用 查看   
  80. #229楼 寻自己      2009-10-27 13:50
    StringWriter sw = new StringWriter();
    HtmlTextWriter htw = new HtmlTextWriter(sw);
    Repeater rpt = new Repeater();
    rpt.DataSource = Comment.GetComments(PageSize,PageIndex,out m_totalCount);
    rpt.DataBind();
    rpt.RenderControl(htw);
    return sw.ToString();

    这个也可以吧,赵老师?
    ----------------------
    忘记看前面的了,原来这个不符合DRY原则
     回复 引用 查看   
  81. #230楼 hailibu      2009-11-03 14:53
    老赵,你好!
    public class ViewManager<T> where T : UserControl 可不可以改成
    public class ViewManager<T> where T : Page

    因为我想用 aspx页面 来生成html,请指教:)
     回复 引用 查看   
  82. #231楼[楼主] Jeffrey Zhao      2009-11-03 14:57
    @hailibu
    我不知道,你试一下吧。
    不过我不明白,既然生成HTML片段,为什么要用Page啊。
     回复 引用 查看   
  83. #232楼 hailibu      2009-11-03 15:26
    老赵响应处理真是快,感动ing

    我刚才试过了,不行啊 :(

    错误提示:类型“ASP.test_aspx”不从“System.Web.UI.UserControl”继承

    return (T)this.m_pageHolder.LoadControl(path);//这句打红色标识

    PS:我想通过 读取动态页面上的HTML 来生成 一个静态页面文件。


     回复 引用 查看   
  84. #233楼[楼主] Jeffrey Zhao      2009-11-18 23:17
    @Anoxia
    用评论的代码插入功能吧。
     回复 引用 查看   
  85. #234楼[楼主] Jeffrey Zhao      2009-11-18 23:18
    @hailibu
    Server.Execute,你可以使用这个。
     回复 引用 查看   
  86. #235楼 Frank.K[未注册用户]2009-11-18 23:37
    老赵您好,我利用这种方法做例子时候碰到一个问题,我在usercontrol中使用了Repeater控件,但是我在Repeater的databind事件中需要做一些相应的处理,实际运行后发现这样的方式并不会激发控件事件,所以我无法在该事件中进行必要的数据处理,请问这种情况有什么办法可以解决???
     回复 引用   
  87. #236楼 Anoxia      2009-11-18 23:46
    @Jeffrey Zhao
    页面通过ajax调用ManageWS(WEBSERVICE)里的loadMenuList方法,加载usercontrol中的数据,为了实现页面中点击某个TAB后动态加载左侧的菜单,此菜单部分是个usercontrol。但是这么做了之后usercontrol中的onitemcommand事件没有触发,实在没办法了,请赵老师指点。代码片段如下:

    usercontrol中代码片段:

    <asp:Repeater ID="rootRp" runat="server" onitemcommand="rootRp_ItemCommand" >
        <ItemTemplate>
            <h2>
                <asp:Label ID="rootName" runat="server" Text='<%#Eval("Name") %>'></asp:Label>
            </h2>
            
            <asp:Repeater ID="menuRp" runat="server">
                <HeaderTemplate>
                    <ul>
                </HeaderTemplate>
                <ItemTemplate>
                    <li>&raquo;&nbsp;
                        <a href='<%#Eval("url") %>' title='<%#Eval("Name") %>'><%#Eval("Name") %></a>
                      
                    </li>
                </ItemTemplate>
                <FooterTemplate>
                    </ul>
                </FooterTemplate>
            </asp:Repeater>
        </ItemTemplate>
    </asp:Repeater>
    


    ManageWS.asmx.cs代码片段:

    [WebMethod(EnableSession = true)]
            public string loadMenuList(string tabId) {
                try {
                    TM.Dealer.Common.ViewManager<menuBack> menuBack = new TM.Dealer.Common.ViewManager<menuBack>();
                    var control = menuBack.loadViewControl("~/manage/usercontrols/menuBack.ascx");
                    control.Id = Convert.ToInt32(tabId);
                    var s = menuBack.RenderView(control);
                    return s;
                }
                catch (Exception ex)
                {
                    return "调用Web服务失败,错误原因:<br/>" + ex.Message;
                }
            }
    


    ViewManager代码片段:

    public class ViewManager<T> where T : UserControl //此处用范型限定了该类只适用于usercontrol
        {
            private Page p;
    
            /// <summary>
            /// 加载用户控件
            /// </summary>
            /// <param name="path"></param>
            /// <returns></returns>
            public T loadViewControl(string path)
            {
                this.p = new MyPage();
                return  p.LoadControl(path) as T;
            }
         
            /// <summary>
            /// 呈现控件内容
            /// </summary>
            /// <param name="control"></param>
            /// <returns></returns>
            public string RenderView(T control)
            {
                StringWriter output = new StringWriter();
    
                this.p.Controls.Add(control);
                System.Web.HttpContext.Current.Server.Execute(this.p, output, false);
                return output.ToString();
            }
    
            //该类用来重写VerifyRenderingInServerForm方法,该方法的控制对呈现服务端控件有很大的影响
            public class MyPage : Page
            {
                public override void VerifyRenderingInServerForm(Control control)
                {
                                    
                    //base.VerifyRenderingInServerForm(control);
                }
            }
        }
    


     回复 引用 查看   
  88. #237楼[楼主] Jeffrey Zhao      2009-11-19 00:05
    @Frank.K
    首先,这样的控件最好不要有复杂的功能,其次,你就这样做吧:
    http://www.cnblogs.com/JeffreyZhao/archive/2009/10/14/about-debugging.html
     回复 引用 查看   
  89. #238楼[楼主] Jeffrey Zhao      2009-11-19 00:06
    @Anoxia
    明天有空了看看。:)
     回复 引用 查看   
  90. #239楼 Frank.K[未注册用户]2009-11-19 00:16
    好的,先谢过,这篇文章我会好好看看的,再次感谢。。。
     回复 引用   
  91. #240楼 Anoxia      2009-11-19 01:16
    @Jeffrey Zhao
    最后用了极丑陋的方式实现了,还是期待赵老师找到好点的解决方案。我将Repeater的onitemdatabound事件去掉了,在Repeater里循环输出JS回调WebService输出string。
    哎,代码片段如下:


    <asp:Repeater ID="rootRp" runat="server" ><%--onitemcommand="rootRp_ItemCommand"--%>
        <ItemTemplate>
            <h2>
                <asp:Label ID="rootName" runat="server" Text='<%#Eval("Name") %>'></asp:Label>
            </h2>
            <ul id='<%#String.Format("menuUl{0}",Eval("MenuId")) %>'>
                
            </ul>
            <script type="text/javascript">
                 $('<%#String.Format("#menuUl{0}",Eval("MenuId")) %>').load("ajaxHelp.aspx", function(result) {
                    //设置loading进度
                    $('<%#String.Format("#menuUl{0}",Eval("MenuId")) %>').html("从服务端获取数据中,请稍候...");
                    //调用WS
                    TMDealerPro.manage.WebService.ManageWS.loadChildMenuList('<%#Eval("MenuId")%>', 
                        function(result){
                            $('<%#String.Format("#menuUl{0}",Eval("MenuId")) %>').fadeIn("slow", function() {
                            $(this).html(result);
                        });
                    });
                }); 
                      </script>
              </ItemTemplate>
    </asp:Repeater>
    
    
     回复 引用 查看   
  92. #241楼[楼主] Jeffrey Zhao      2009-11-19 09:04
    @Anoxia
    岂不是很慢?
     回复 引用 查看   
  93. #242楼 Anoxia      2009-11-19 09:47
    @Jeffrey Zhao
    看你这贴心动就想用,但感觉在具体这个业务逻辑上这么用反而化简为繁了,呵呵。用通常的做法,套UpdatePanel服务端控制呈现很简单的。不过,这种情况下,赵老师如果有new idea,期待中...
     回复 引用 查看   
  94. #243楼[楼主] Jeffrey Zhao      2009-11-19 23:16
    @Anoxia
    为什么要触发onitemcommand?
    其实这个方法只是用来生成HTML的,不是用来做前后台交互的……
     回复 引用 查看   
  95. #244楼 俊采星驰      2009-12-27 20:51
    请问楼主我按照你给的代码试了下没加AJAX前没有问题,但加了之后点击下一页时数据还是原来的那些没有改变。我打断点调试时后台代码也是已经执行了的,而且也重新绑定了ItemComments.ascx控件,但在前台就是没法更新。请楼主指点迷津~~
     回复 引用 查看   
  96. #245楼 俊采星驰      2009-12-27 21:03
    看了评论才知道原来忘记给div加id了,还以为id是没用的呢,真是郁闷了我半天!!
     回复 引用 查看   
  97. #246楼 e狐      2009-12-27 23:22
    赵老师,请教个问题。。你上面的例子中GetComments这个Handler中
    ViewManager<ItemComments> viewManager = new ViewManager<ItemComments>();
    ItemComments control = viewManager.LoadViewControl("~/ItemComments.ascx");

    这个里面的ItemComments 类型是怎么引用到的??我试过都放在一个命名空间里面也引用不到啊。。
     回复 引用 查看   
  98. #247楼 e狐      2009-12-27 23:33
    是不是上面的代码仅仅是个例子啊?我是不是一定能够要建个ClassLibrary放入ItemComments的后台实现与GetComments,然后用站点引用这个ClassLibray才行啊?
     回复 引用 查看   
  99. #248楼[楼主] Jeffrey Zhao      2009-12-27 23:46
    @e狐
    总之,你引用到就行了——这个是ASP.NET基础吧……
     回复 引用 查看   
  100. #249楼 伊牛娃      2010-03-15 11:32
    ItemComments.aspx
    应该是ItemComments.ascx
     回复 引用 查看   
  101. 评论共3页: 上一页 1 2 3 
发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

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

0 1021297 GjhKYp6S3xg=