一起谈.NET技术,使用User Control做HTML生成

  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之后效果如下:

  这张页面的功能非常简单,那就是察看评论。当前评论的页码会使用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本身不会带来任何额外的内容。

时间: 2024-08-01 05:08:30

一起谈.NET技术,使用User Control做HTML生成的相关文章

一起谈.NET技术,VS2010如何做WAP开发

自从VS2010 RC出来以后,就顺便把机器重做了,重装的时候特意没安装VS2008,全线过渡到VS2010和.net4.0,但今天有一小WAP项目,才意识到VS2010貌似不支持WAP窗体,按照记忆参考以前VS2008的做法(见:wap开发体会),下载模板后,一时眼花即没找到新建Mobile Web窗体的地方,于是又把VS2008装回来了,等到把VS2008搞定后,怀着一颗不死之心又重新到VS2010里瞅了瞅,发现其实也可以的(哎,真想抽自己),见下图: 项目上右击-->Add-->New

使用User Control做HTML生成

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

一起谈.NET技术,ASP.NET MVC 2生成动态表单的一种最简单的思路

在BPM.OA等系统中,都会存在一个表单设计器.有些是通过操作gridview来完成一个表单的设计:有些是通过类似VS拖拽的方法完成一个表单的设计.很明显后面一种优越于前面一种.无论是哪种,最后都会产生一些XML之类的表单结构的数据. 这篇文章将讲述,在表单设计器设计好表单之后,在ASP.NET MVC中如何将表单结构的xml转换成实际应用系统中的表单.看下面一个xml文件,我们假设它是由一个表单设计器设计出来的. <?xml version="1.0" encoding=&qu

一起谈.NET技术,看看Entity Framework 4生成的复杂的分页SQL语句

之前发现Entity Framework 4生成的COUNT查询语句问题,今天又发现它生成的分页SQL语句问题,而LINQ to SQL却不存在这个问题. >>> 来看一看,瞧一瞧! 上代码: 看生成的SQL语句: 1. Entity Framework生成的SQL: 一个TOP,三个FROM. 2. LINQ to SQL生成的SQL: 无TOP,两个FROM. 两者的差距一目了然. >>> 再来看一个: 将上面代码中Where的查询条件改为常量,即Where(cod

《创业家》牛文文:少谈点模式多谈点技术

"模式"如同当年的"主义",流行于各种创业大赛.创业励志节目.论坛的"街头"式秀场 文/创业家 牛文文 "美国某某公司你知道吧?就是刚被戴尔.惠普.思科十几亿美元抢购的那家.我们的模式和它的一样,现在还没赢利,可将来起码有十几亿人民币的市值." "我开了小煤矿,但煤运不出去,上商学院之后受到启发,想搞模式创新,具体讲就是想在铁路边上搞个煤炭物流开发区,建一个大的物流和信息流平台,把分散的煤炭集中在我这个园区,这样和铁

oracle-Oracle BIEE技术Oracle BIEE能做鼠标悬停提示的那种效果吗?

问题描述 Oracle BIEE技术Oracle BIEE能做鼠标悬停提示的那种效果吗? 请问下各位大神,Oracle BIEE能做鼠标悬停提示的那种效果吗? 解决方案 自己来回答,请参考这篇文章http://blog.csdn.net/windfo/article/details/6222111 解决方案二: Oracle BIEE 没用过,关注一下,期待哪位大神出手 解决方案三: 估计够呛,我上次提这个问题.管理员直接给删掉了.

请问这种图用什么技术或插件可以做出来?

问题描述 请问这种图用什么技术或插件可以做出来? 解决方案 这张图的出处是在哪里呢? 只是作图的话,powrpoint里就有好些常用模板,其他的模板库你还可以自己下载的~ 解决方案二: 绘图软件都能绘制吧,要自动生成有些麻烦些.

控件-在MFC中使用List Control做表格

问题描述 在MFC中使用List Control做表格 在基于对话框的MFC中添加了一个List Control控件,属性设置为report,显示栅格GridLines,用它来做一个4行10列的表格,现在是显示的10列,但行数是自动显示了很多行,要怎样设置才能让它只显示4行呢? 解决方案 MFC如何使用 List ControlMFC list control 的使用MFC List Control滚动条 解决方案二: 默认的网格就是这么绘制的.你要没有数据的地方不绘制网格,可以换别的控件,或者

网页-用什么技术可以实现asp页面自动生成htm页面?

问题描述 用什么技术可以实现asp页面自动生成htm页面? 我的网站是用asp编写的,如何做到:在网页内容创建或者更新时自动生成静态HTML页面,以后的浏览全部引向静态页面?用什么技术去实现?可以向我推荐一些书籍! 解决方案 这个不是asp干的活,可以通过其他发布程序可以直接生成html页面的.百度下吧,现在发布成html页面的系统也比较多.asp页面最多是通过前端的web服务,可以配置成页面缓存. 解决方案二: 你可以是试一下asp.net的页面静态化,还是要写程序自动生成的 解决方案三: 全