博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
技巧:使用User Control做HTML生成
阅读量:6249 次
发布时间:2019-06-22

本文共 5963 字,大约阅读时间需要 19 分钟。

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本身不会带来任何额外的内容。
本文转自 jeffz 51CTO博客,原文链接:http://blog.51cto.com/jeffz/59534,如需转载请自行联系原作者
你可能感兴趣的文章
hdu 1009 FatMouse&#39; Trade
查看>>
Filter过滤器
查看>>
Percona-Server/MySQL响应时间统计
查看>>
Curl POST to HTTPS url gives SSLRead() error:curl: (56) SSLRead() return error -9806
查看>>
WKWebView的Cookie问题小记
查看>>
Vue全家桶实战篇
查看>>
yii2-websocket | 基于 yii2 实现的 WebSocket 扩展
查看>>
在vue中使用Autoprefixed
查看>>
[译] Android Dev Summit 2018 应用(instant app 的总结 + 开源)
查看>>
Swift那些事之-代理和闭包传值
查看>>
GitHub优秀三方库推荐以及日常知识积累分享
查看>>
关于如何给for循环遍历出来的每一项注册事件并且在注册的事件里面打印出对应的下标...
查看>>
[小程序] mpVue 踩坑
查看>>
【其他】为公司项目个性化 Git 设置
查看>>
Android加快编译速度的另一种方法
查看>>
常见的Node.js攻击-恶意模块的危害
查看>>
Netty从没听过到入门 -- 服务器端详解(什么是netty?netty的应用场景是什么?netty的简易使用)...
查看>>
不要使用SBJSON(json framework)
查看>>
2019-05-15T02:28:01.000+0000
查看>>
记录数组常用方法汇总
查看>>