轉帖|其它|編輯:郝浩|2011-03-17 13:40:33.000|閱讀 1038 次
概述:本系列是講解如何在asp.net mvc中對數據進行展示、排序、分頁等的系列文章。在上周的文章中,一步一步教會了大家如何使用ASP.NET MVC框架去的展示數據。在上周的文章中,我們先用Visual Studio創建了一個新的ASP.NET MVC應用程序,接著連接到了Northwind數據庫,并展示了如何使用微軟的LINQ-SQL的工具去訪問數據庫中的數據,接著指導如何去實現視圖層去展示產品信息及如何設計控制器。
# 界面/圖表報表/文檔/IDE等千款熱門軟控件火熱銷售中 >>
本系列是講解如何在asp.net mvc中對數據進行展示、排序、分頁等的系列文章。在上周的文章中,一步一步教會了大家如何使用ASP.NET MVC框架去的展示數據。在上周的文章中,我們先用Visual Studio創建了一個新的ASP.NET MVC應用程序,接著連接到了Northwind數據庫,并展示了如何使用微軟的LINQ-SQL的工具去訪問數據庫中的數據,接著指導如何去實現視圖層去展示產品信息及如何設計控制器。
本文是在上一篇文章的例子基礎上,展示了如何去實現數據的雙向排序。如果你是已經熟悉ASP.NET WebForm開發的開發者,你應該知道,在GridView控件中可以很簡單的通過點擊就能實現排序。但可惜的是,在ASP.NET MVC中實現排序的功能并不是那么簡單,但工作量也沒有顯著增多。在ASP.NET MVC中,我們能更多地控制網格和排序的界面布局和標記,以及通過何種機制來實現排序。以往使用GridView控件時,排序是通過將參數以POSTBACK的形式回傳到后臺,以決定用什么樣的列進行排序以及是以升序或降序-排列,回傳的參數作為隱藏表單域提交。在本文中,我們將使用查詢字符串參數來指定排序參數,這意味著排序的順序可以被搜索引擎搜索到,能通過電子郵件發送給同事,還能做很多GridView內置排序功能不能實現的事情。
與上一篇文章一樣,本文提供了分步的指導說明,包括一個完整的可以工作的代碼例子,在文章末尾可以下載。
步驟0:一個簡要指南
本文將介紹如何實現雙向的排序,并假定讀者已經閱讀掌握了上一篇文章介紹的內容。
在上一篇文章中,我們可以通過ASP.NET MVC中的URL轉發功能,以下面的地址形式訪問產品的金喜正規買球:
www.yoursite.com/Products/Index(可以簡寫成www.yoursite.com/Products)。本文中,我們將用下面的URL去訪問要排序的內容:
www.yoursite.com/Products/Sortable?sortBy=ColumnNameascending=true|false。
具體的一些例子如下:
/Products/Sortable- 這表示按默認順序排列產品。當沒指定排序的列時,默認按產品名稱的字母順序(如升序)排列。換句話說,如果SortBy參數沒有提供,按產品名稱排序,如果不提供ascending參數,按遞增順序排列。
/Products/Sortable?sortBy=UnitPrice 按單價的升序排序產品。
/Products/Sortable?sortBy=UnitPriceascending=false – 按UnitPrice列降序排序(即從最昂貴的到價格最便宜的)。
跟使用GridView控件一樣,在點擊網格中標題行中的列名時可以進行。但不同于GridView的是,我們每次點列的這些標題,是以超鏈接的形式實現的,并且帶有參數,比
如表格中有價格這個列,當第一次點擊列名時,將以
www.yoursite.com/Products/Sortable?sortBy=UnitPriceascending=true的形式發送鏈接到后端,請注意的是,在網格中顯示的列名,不一定跟在URL中sortBy查詢字符串參數中傳遞的名稱是一樣的。sortBy 參數提供的是在數據庫中的列名,兩者并不要求一定相同。
步驟1:創建指定的視圖模型
在上一篇文章的演示中,我們使用了產品的集合作為其實體模型(以NorthwindDataContext去命名)。這對簡單的網格來說是可以的,但對于要排序的數據表格,需要知道一點的不僅僅是產品的集合,還要視圖層方面知道哪些列的數據需要進行排序,是按升序或降序排序,如果用戶要點的列已經按某一個順序已排序的話,則此時會按原來的順序排序(假設某列已經是按升序排列,用戶點標題一次,則倒過來按降序排列,再點一次,又按升序,如此類推)。
為此,我們添加一個新類,這些類被稱為特定視圖服務的模型,打開上一篇文章中已經實現的應用程序,在Models文件夾下添加一個名為ProductGridModel.cs 的文件,代碼如下:
namespace Web.Models
{
public class ProductGridModel
{
// Data properties
public IEnumerable Product Products { get; set; }
// Sorting-related properties
public string SortBy { get; set; }
public bool SortAscending { get; set; }
public string SortExpression
{
get
{
return this.SortAscending ? this.SortBy + " asc" : this.SortBy + " desc";
}
}
}
}
該ProductGridModel類定義了一個產品屬性,它是一個集合類的屬性,用來顯示產品集合,同時也有三種排序相關的屬性:
SortBy
在數據庫中用來排序的數據列名稱
SortAscending
一個布爾值,指示是否用升序排序數據
SortExpression
只讀屬性,返回一個排序字符串,其構造為SortBy和SortAscending值的組合。
例如,如果SortBy分為UnitePrice和SortAscending是true,SortExpression的值為
UnitedPrice asc。
如果SortBy設置為Discontinued 和SortAscending是false,則SortExpression返回Discontinued desc 。
步驟2:創建Sortable 的Action方法
在上一篇教程中,我們創建了一個名為ProductsController的控制器,其中有一個叫index的action和一個輔助屬性DataContext,本文中,我們將添加一個新的action方法到控制器中,并命名為Sortable,當有來自如www.yoursite.com/Products/Sortable的請求時,則執行該排序方法。
ASP.NET MVC中實現了自動參數綁定,來自URL或其他的參數請求,將被映射到執行的實際的action中去。例如,如果你在控制器action中定義了一個輸入參數,名為sortBy,則MVC框架將搜索傳入的請求的參數,看是否有任何具有相同名稱的參數(在這里,一個參數可能是一個提交表單域,一個查詢字符串參數或路由參數。)如果找到一個匹配,則自動把參數的值得賦給action中定義的參數。
下面是其實際代碼:
public class ProductsController : Controller
{
...
// GET: /Products/Sortable?SortColumn=columnNameAscending=true|false
public ActionResult Sortable(string sortBy = "ProductName", bool ascending = true)
{
var model
= new ProductGridModel()
{
SortBy
= sortBy,
SortAscending
= ascending
};
model.Products
= this.DataContext.Products.OrderBy(model.SortExpression);
return View(model);
}
}
請注意,action中接受兩個輸入參數:sortBy和ascending。任何來自URL的請求,只要符合這兩個參數的名稱的,其值得都會被自動匹配傳入到該action中去, 也就是說,如果有人訪問www.yoursite.com不指定查詢字符串參數,則默認按照產品的名稱進行升序排序。
Sortable的action首先創建一個新的ProductGridModel實例,命名為model,并且對model的SortBy和SortAscending屬性進行賦值,接著,Sortable action獲得了產品的集合(this.DataContext.Products),并調用其中的OrderBy方法進行排序,而排序的參數表達式正好是SortExpression。最后,將model模型返回給一個強類型的視圖,。
如果您熟悉使用LINQ,你會覺得我在這里使用的OrderBy方法有點奇怪,你可能會用LINQ中的lambda表達式如下這樣寫,如:this.DataContext.Products.OrderBy(p =p.ProductName)。
OrderBy方法,我使用的是不標準的LINQ,相反是微軟的動態LINQ庫中的方法
(//weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx),它是一個庫允許使用字符串做為查詢參數。本文附件中提供了一個名為Dynamic.cs的文件(它在HelperClasses文件夾中),其中包含了OrderBy擴展方法簽名,大家可以去學習一下。
步驟3:創建視圖
鼠標右鍵點控制器中的Sortable action,在彈出的菜單中選擇添加視圖選項。從添加視圖對話框中,選中Create a strongly-typed view 復選框,然后從View data class 下拉選擇Web.Models.ProductGridModel,點擊確定。這時會創建視圖Sortable.aspx,如下圖:
接下來,修改代碼如下:
table class="grid"
tr
thProduct/th
thCategory/th
thQty/Unit/th
thPrice/th
thDiscontinued/th
/tr% foreach (var item in Model.Products) { %
tr
td class="left"%: item.ProductName %/td
td class="left"%: item.Category.CategoryName %/td
td class="left"%: item.QuantityPerUnit %/td
td class="right"%: String.Format("{0:C}", item.UnitPrice) %/td
td
% if (item.Discontinued) { %
img src="%=Url.Content("~/Content/cancel.gif") %" alt="Discontinued" title="Discontinued" /
% } %
/td
/tr
% } %/table
上面的代碼跟上一篇文章中你看到的基本沒什么區別,唯一的區別,其實是在foreach語句中,在上一篇文章中,是:
▲
foreach(var item in Model) { ... },而本文中,其模型變成了ProductGridModel實例,其產品屬性包含產品的集合。因此,在foreach循環遍歷Model.Products。運行后,你會發現訪問頁面時默認的是按產品的升序排列的:
現在網格的列標題是文本,等下我們會將其修改為超級鏈接,但現在我們也可以馬上在瀏覽器中,通過輸入的方法體驗下了,舉例來說,如果你輸入:
www.yoursite.com/Products/Sortable?sortBy=UnitPriceascending=false- 你應該看到按 照價格從高到低排列的產品。
在網格中添加排序鏈接
此時,用戶無法直觀地去排序,除非他在瀏覽器中象上面的方法輸入查詢字符串,因此我們在每個網格的標題行中提供超鏈接指向適當的網址。乍一看,這可能會像一個簡單的任務,比如產品名稱列,其url應該是
Products/Sortable?sortBy=ProductNameascending=true,而價格列,則應該是
Products/Sortable?sortBy=UnitPriceascending=true,如此類推,歸納一下,其URL的連接方式應該象:
Products/Sortable?sortBy=ColumnNameascending=false,同時,還應該增加一個象圖標這樣的指示,讓用戶可以清楚地看到當前排序的情況。
要做到這一點,需要創建一個局部視圖。一個局部視圖看上去跟WebForms模型中的用戶控件的概念差不多。總之,這是一可以重用的視圖。
在解決方案資源管理器中的Views/Shared目錄下,鼠標右鍵點擊,在彈出的菜單中選擇添加視圖,將其命名為SmartLink,并選中下面的兩個復選框,然后選擇Web.Models.ProductGridModel作為其view data class,如下圖。
單擊確定后,Visual Studio將創建一個新的局部視圖(SmartLink.ascx)。其中局部視圖只包含下列標記:
%@Control
Language="C#" Inherits="System.Web.Mvc.ViewUserControlWeb.Models.ProductGridModel" %
我們現在來學習如何將局部視圖添加到正常的視圖中去,這可以使用下列Html.RenderParial方法之一:
% Html.RenderPartial("partialViewName"); %
% Html.RenderPartial("partialViewName", viewData); %
% Html.RenderPartial("partialViewName", model); %
% Html.RenderPartial("partialViewName", model, viewData); %
可選的參數是一個ViewDataDictionary的ViewData對象。如果提供,可以通過
ViewData["name"].去訪問這些值。
當要展示局部視圖的時候,我們還需要將ProductGridModel和一些額外的信息傳進來,下面的代碼展示了對于產品名稱一列這個表頭,我們是如何通過局部視圖去產生的,注意的是,我們通過ViewDataDictionary可以設置表頭中顯示的列的名稱以及該列實際對應的是數據庫中的列名:
% Html.RenderPartial("SmartLink", Model, new ViewDataDictionary {
{ "ColumnName", "ProductName" }, { "DisplayName", "Product" } }); %
這里設置了列名是ProductName,而顯示在表頭中的列名是Product。同理,其他表頭列也是這樣的處理,就可以把Model傳遞到部分視圖中來了。
table class="grid"
tr
th% Html.RenderPartial("SmartLink", Model, new ViewDataDictionary { { "ColumnName", "ProductName" }, { "DisplayName", "Product" } }); %/th
th% Html.RenderPartial("SmartLink", Model, new ViewDataDictionary { { "ColumnName", "Category.CategoryName" }, { "DisplayName", "Category" } }); %/th
th% Html.RenderPartial("SmartLink", Model, new ViewDataDictionary { { "ColumnName", "QuantityPerUnit" }, { "DisplayName", "Qty/Unit" } }); %/th
th% Html.RenderPartial("SmartLink", Model, new ViewDataDictionary { { "ColumnName", "UnitPrice" }, { "DisplayName", "Price" } }); %/th
th% Html.RenderPartial("SmartLink", Model, new ViewDataDictionary { { "ColumnName", "Discontinued" }, { "DisplayName" , "Discontinued" } }); %/th
/tr
...
/table
現在我們可以看到,從局部視圖中可以通過Model屬性訪問在Sortable視圖中的任何值了,訪問的方法是通過ViewData[name]即可。還要記得,我們之所以要在這里使用局部視圖,其目的為某個特定的列生成超級鏈接。
我們接下來要判斷某個列是否要升序還是降序排列,下面的代碼創建了isDescending變量,這個值只有當要排序的列(由Model.SortBy產生)和數據庫中的列名相同(由ViewDate[ColumnName]產生)時,并且當前的排序為升序(Model.SortAscending)時,其值才為True。
var isDescending = string.CompareOrdinal(Model.SortBy,
ViewData["ColumnName"].ToString()) == 0 Model.SortAscending;
同時,我們最好通過設置CSS樣式,告訴用戶當前的排序方向,因此我們在 CustomStyles.css的CSS類定義了兩個CSS類-sortAsc和sortDesc,增加了一個向上或向下箭頭,可以用如下代碼去判斷:
f (string.CompareOrdinal(Model.SortBy, ViewData["ColumnName"].ToString()) == 0)
{
if (Model.SortAscending)
htmlAttributes.Add("class", "sortAsc");
else
htmlAttributes.Add("class", "sortDesc");
最后,完成的局部視圖代碼如下:
%
var isDescending = string.CompareOrdinal(Model.SortBy, ViewData["ColumnName"].ToString()) == 0 Model.SortAscending;
var routeData
= new RouteValueDictionary { { "sortBy", ViewData["ColumnName"].ToString() }, { "ascending", !isDescending } };
var htmlAttributes
= new Dictionarystring, object();
if (string.CompareOrdinal(Model.SortBy, ViewData["ColumnName"].ToString()) == 0)
{
if (Model.SortAscending)
htmlAttributes.Add("class", "sortAsc");
else
htmlAttributes.Add("class", "sortDesc");
}
%%: Html.ActionLink(
ViewData["DisplayName"].ToString(), // Link Text
Html.ViewContext.RouteData.Values["action"].ToString(), // Action
Html.ViewContext.RouteData.Values["controller"].ToString(), // Controller
routeData, // Route data
htmlAttributes // HTML attributes to apply to hyperlink
)
%
Html.ActionLink方法會向瀏覽器端產生超鏈接的HTML。它的第一個參數為要顯示的文字超鏈接的標題,這里通過ViewData[displayname]讀取。
第二個和第三個參數指定了action和控制器去生成鏈接,我們使用Html.ViewContext.RouteData.Values[action].ToString
和Html.ViewContext.RouteData.Values[controller].ToString()來獲得當前請求的action和控制器,而避免了硬編碼。
第四個輸入參數指定了路由的數據,這是我們用RouteValueDictionary字典的形式,分別向sortBy和ascending參數傳入值。
最后的參數設定了生成鏈接的HTML的CSS樣式。
當運行程序后,默認是按產品名稱升序排序的,如下圖。
當點產品標題列旁邊的小箭頭時,會向控制器發出如
Products/Sortable?sortBy=ProductNameascending=False的請求,于是產品名稱按降序排序了,如下圖:
本站文章除注明轉載外,均為本站原創或翻譯。歡迎任何形式的轉載,但請務必注明出處、不得修改原文相關鏈接,如果存在內容上的異議請郵件反饋至chenjj@fc6vip.cn
文章轉載自:IT168