技巧:使用User Control做HTML生成

User Control大家肯定不會陌生,在使用ASP.NET的過程中,除了aspx頁面,最常見的就莫過于ascx了。ascx是一個有獨立邏輯的組件,提供了強大的復用特性,合理使用,能夠大大提高開發效率。通過User Control直接生成HTML內容其實已經是一個比較常用的技巧了(尤其在AJAX時代),不過網絡上這方面的內容比較少,很多人還是在苦苦地拼接字符串,因此在這里我通過一個實例簡單介紹一下這個技巧。
對一個對象(文章,圖片,音樂,etc.)進行評論是應用中最常見的功能之一。首先,我們定義一個Comment類,以及其中會用到的“獲取”方法:
publicpartial?class?Comment
{
public?DateTime?CreateTime {?get;?set; }

public?string?Content {?get;?set; }
}

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 {?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: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.pswp.cn/news/392621.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/392621.shtml
英文地址,請注明出處:http://en.pswp.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 …