技巧:使用User Control做HTML生成

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

public string Content { getset; }
}

publicpartial 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:Repeaterrunat="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>
<%%>
publicpartial 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 { getset; }

public int PageSize { getset; }

private int m_totalCount;
public int TotalCount
{
get
{
return this.m_totalCount;
}
}
}
然后再页面(ViewItem.aspx)中使用这个组件:
<div id="comments"><demo:ItemCommentsID="itemComments" runat="server" /></div>
publicpartial 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):
publicclass 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类,我们来看一下它是怎么实现的:
publicclass 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函数。例如“上一页”的代码就会变成:
<ahref="/ViewItem.aspx?page=<%= this.PageIndex - 1 %>" title="上一页"
onclick="return getComments(<%= this.PageIndex - 1 %>);">上一页</a>
第二,就是实现getComments这个客户端方法。在这里我使用了prototype框架,好处就是能够用相当简洁的代码来做到替换HTML的AJAX效果:
<scripttype="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本身不会带来任何额外的内容。


本文转自 jeffz 51CTO博客,原文链接:http://blog.51cto.com/jeffz/59534,如需转载请自行联系原作者

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/392621.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Boot干货系列:(二)配置文件解析

前言 上一篇介绍了Spring Boot的入门&#xff0c;知道了Spring Boot使用“习惯优于配置”&#xff08;项目中存在大量的配置&#xff0c;此外还内置了一个习惯性的配置&#xff0c;让你无需手动进行配置&#xff09;的理念让你的项目快速运行起来。所以&#xff0c;我们要想把S…

mysql常用操作记录

&#xff08;1&#xff09;判断表中一个字段是空&#xff0c;可为&#xff1a;字段名 IS NULL&#xff08;2&#xff09;类似oracle的decode作用&#xff1a;IF(字段名>0,字段名,0)&#xff08;3&#xff09;时间格式&#xff08;年-月-日&#xff09;&#xff1a;DATE_FORM…

小爱音响调用php接口_阿里API调用二(PHP)

接口地址拼合成功后&#xff0c;用curl函数post获取阿里返回的完整数据&#xff0c;将地址传入getContent()方法中&#xff0c;绝对能获取用户数据。public function getContent($url){$ch curl_init();// 初始化curl_setopt($ch,CURLOPT_URL,$apiprourlall);curl_setopt($ch,…

leetcode 452. 用最少数量的箭引爆气球(贪心算法)

在二维空间中有许多球形的气球。对于每个气球&#xff0c;提供的输入是水平方向上&#xff0c;气球直径的开始和结束坐标。由于它是水平的&#xff0c;所以纵坐标并不重要&#xff0c;因此只要知道开始和结束的横坐标就足够了。开始坐标总是小于结束坐标。 一支弓箭可以沿着 x…

javascript编程题_如何开始使用JavaScript进行竞争性编程

javascript编程题by Priyabrata Biswas通过Priyabrata Biswas 如何开始使用JavaScript进行竞争性编程 (How to get started with Competitive Programming in JavaScript) If you’re not familiar with competitive programming, basically it is a mind sport with the aim …

hibernate Criteria(条件查询接口)

Criteria&#xff08;条件查询接口&#xff09; // 1.简单查询 List<Customer> list session.createCriteria(Customer.class).list();// 2.条件查询: Criteria criteria session.createCriteria(Customer.class); criteria.add(Restrictions.eq("name",&quo…

ElastciSearch简单总结(笔记)

前言&#xff1a; 前段时间在项目中使用了es,作为一个当前比较流行的分布式搜索引擎&#xff0c;在学习和使用它的过程中&#xff0c;踩了不少坑&#xff0c;这篇文章先简单整理了一下&#xff0c;后续会整理一下之前踩过的一些坑。 1. ElastciSearch是什么 ElasticSearch是一…

记一次ArrayList产生的线上OOM问题

前言&#xff1a;本以为(OutOfMemoryError)OOM问题会离我们很远&#xff0c;但在一次生产上线灰度的过程中就出现了Java.Lang.OutOfMemoryError:Java heap space异常&#xff0c;通过对线上日志的查看&#xff0c;最终定位到ArrayList#addAll方法中&#xff0c;出现这个问题的原…

leetcode 222. 完全二叉树的节点个数(dfs)

给出一个完全二叉树&#xff0c;求出该树的节点个数。说明&#xff1a;完全二叉树的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都集中在该层最左边的若干位置。若最底…

css 计算属性的应用_如何使用一点CSS Grid魔术设计计算器应用

css 计算属性的应用by Deepika Gunda由Deepika Gunda 如何使用一点CSS Grid魔术设计计算器应用 (How to use a little CSS Grid magic to design a calculator app) This article is a quick intro to CSS Grid. We will be making a calculator using it.本文是CSS Grid的快速…

vc调试大全

一、调试基础 调试快捷键 F5&#xff1a; 开始调试 ShiftF5: 停止调试 F10&#xff1a; 调试到下一句&#xff0c;这里是单步跟踪 F11&#xff1a; 调试到下一句&#xff0c;跟进函数内部 ShiftF11: 从当前函数中跳出 CtrlF10: 调试到光标所在位置 F9&#xff1a; …

Google-Guava-EventBus源码解读

Guava是Google开源的一个Java基础类库&#xff0c;它在Google内部被广泛使用。Guava提供了很多功能模块比如&#xff1a;集合、并发库、缓存等&#xff0c;EventBus是其中的一个module&#xff0c;本篇结合EventBus源码来谈谈它的设计与实现。 概要 首先&#xff0c;我们先来预…

leetcode 1370. 上升下降字符串

给你一个字符串 s &#xff0c;请你根据下面的算法重新构造字符串&#xff1a; 从 s 中选出 最小 的字符&#xff0c;将它 接在 结果字符串的后面。 从 s 剩余字符中选出 最小 的字符&#xff0c;且该字符比上一个添加的字符大&#xff0c;将它 接在 结果字符串后面。 重复步骤…

mysql 设置事物自动提交_mysql事务自动提交的问题

1&#xff1a;mysql的aut0commit配置默认是开启的&#xff0c;也就是没执行一条sql都会提交一次&#xff0c;就算显示的开启事务也会导致多条SQL不在一个事务中&#xff0c;如果需要相关的SQL在同一个事务中执行&#xff0c;那么必须将autocommit设置为OFF&#xff0c;再显式开…

rest laravel_如何通过测试驱动开发来构建Laravel REST API

rest laravelby Kofo Okesola由Kofo Okesola 如何通过测试驱动开发来构建Laravel REST API (How to build a Laravel REST API with Test-Driven Development) There is a famous quote by James Grenning, one of the pioneers in TDD and Agile development methodologies:T…

python之numpy

numpy是一个多维的数组对象&#xff0c;类似python的列表&#xff0c;但是数组对象的每个元素之间由空格隔开。 一、数组的创建 1.通过numpy的array(参数)&#xff0c;参数可以是列表、元组、数组、生成器等 由arr2和arr3看出&#xff0c;对于多维数组来说&#xff0c;如果最里…

git 上传

转载于:https://www.cnblogs.com/benbentu/p/6543154.html

Liferay 部署war包时候的deployDirectory 细节分析

引入&#xff1a; 在上文中&#xff0c;我们从宏观上讲解了Liferay部署war包的动作是如何触发监听器并且完成部署过程的&#xff0c;但是其中最核心的一块deployDirectory我们没讲&#xff0c;它的作用是当有了临时目录并且已经把war包的内容展开到该目录之后&#xff0c;是如何…

leetcode 164. 最大间距(桶排序)

给定一个无序的数组&#xff0c;找出数组在排序之后&#xff0c;相邻元素之间最大的差值。 如果数组元素个数小于 2&#xff0c;则返回 0。 示例 1: 输入: [3,6,9,1] 输出: 3 解释: 排序后的数组是 [1,3,6,9], 其中相邻元素 (3,6) 和 (6,9) 之间都存在最大差值 3。 示例 2: …

批处理定时mysql备份数据库_定时备份mysql数据库的批处理

定时备份mysql数据库的批处理代码&#xff0c;保存为backup_mysql.bat&#xff0c;运行即可。复制代码 代码如下:echo offset txt1%date:~0,4%::当前年set txt2%date:~5,2%::当前月set txt3%date:~8,2%::当前日set txt4%time:~0,2%::当前小时set txt5%time:~3,2%::当前分钟set …