How is the MVC Framework supposed to invoke your List() method when it doesn’t know what value to supply for page?Change the URL in your browser to http://localhost:xxxxx/?page=1 or http://localhost:xxxxx/?page=2 .You’ll find that it works, and your application will select and display the relevant page of results. §4.7.1 Assigning a Default Parameter Value It’s very easy to tell ASP.NET MVC what value to use for an action method parameter if no other value is\ available. Update ProductsController’s List() method signature by applying a [DefaultValue] attribute:
public ViewResult List([DefaultValue(1)] int page)
{
return View(productsRepository.Products.ToList()
.Skip((page - 1) * PageSize)
.Take(PageSize)
.ToList()
);
}
§4.7.2 Displaying Page Links
We’re going to pass information about the number of pages, the current page index, and so on to the HTML helper using a small model class called PagingInfo. Add the following class to your SportsStore.WebUI project’s Models folder:
namespace SportsStore.Models
{
public class PagingInfo
{
public int TotalItems { get; set; }
public int ItemsPerPage { get; set; }
public int CurrentPage { get; set; }
public int TotalPages
{
get { return (int)Math.Ceiling((decimal)TotalItems / ItemsPerPage); }
}
}
}
This isn’t a domain model. We call these simple objects are sometimes called data transfer objects (DTOs),
Now we can implement the PageLinks HTML helper method. Create a new folder in your SportsStore.WebUI project called HtmlHelpers, and then add a new static class called PagingHelpers.
namespace SportsStore.HtmlHelpers
{
public static class PagingHelpers
{
public static MvcHtmlString PageLinks(this HtmlHelper html,
PagingInfo pagingInfo,
Func<int, string> pageUrl)
{
StringBuilder result = new StringBuilder();
for (int i = 1; i <= pagingInfo.TotalPages; i++)
{
TagBuilder tag = new TagBuilder("a"); // Construct an <a> tag
tag.MergeAttribute("href", pageUrl(i));
tag.InnerHtml = i.ToString();
if (i == pagingInfo.CurrentPage)
tag.AddCssClass("selected");
result.AppendLine(tag.ToString());
}
return MvcHtmlString.Create(result.ToString());
}
}
}
Then we add something into the List.aspx as follows:
<%= Html.PageLinks(
new PagingInfo { CurrentPage = 2, TotalItems = 28, ItemsPerPage = 10 },
i => Url.Action("List", new{ page = i})
) %>
§4.7.3 Making the HTML Helper Method Visible to All View Pages
Rather than copying and pasting that same declaration to all ASPX views that use PageLinks(), ,how about registering the SportsStore.WebUI.HtmlHelpers namespace globally? Open Web.config and find the namespaces node inside system.web/pages. Add your HTML helper namespace to the bottom of the list:
<namespaces>
<add namespace="System.Web.Mvc"/>
<add namespace="System.Web.Mvc.Ajax"/>
...etc...
<add namespace="SportsStore.WebUI.HtmlHelpers"/>
</namespaces>
§4.7.4 Supplying a Page Number to the View
Because you don’t know how many pages there are and what page number it’s displaying. So, you need to enhance the controller to put that extra information into ViewData.Model.
Maybe you will use ViewData as a dictionary which we talked before.But it is loosely typed. So here we add the following class to your SportsStore.WebUI project’s Models folder to encapsulate all the data that List needs to send to its view
namespace SportsStore.WebUI.Models
{
public class ProductsListViewModel
{
public IList<Product> Products { get; set; }
public PagingInfo PagingInfo { get; set; }
}
}
Now you can update the List() method to supply a ProductsListViewModel to its view:
public ViewResult List([DefaultValue(1)] int page)
{
var productsToShow = productsRepository.Products;
var viewModel = new ProductsListViewModel()
{
Products = productsToShow.Skip((page - 1) * PageSize).Take(PageSize).ToList(),
PagingInfo = new PagingInfo
{
CurrentPage = page,
ItemsPerPage = PageSize,
TotalItems = productsToShow.Count()
}
};
return View(viewModel);
}
Also ,you need remember we must change the reciever’s Module type:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master"
Inherits="ViewPage<SportsStore.WebUI.Models.ProductsListViewModel>" %>
And the rest of that view:
<% foreach(var product in Model.Products) { %>
<div class="item">
<h3><%= product.Name %></h3>
<%= product.Description %>
<h4><%= product.Price.ToString("c") %></h4>
</div>
<% } %>
<div class="pager">
<%= Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new {page = x})) %>
</div>
§4.7.5 Improving the URLs
Currently, SportsStore uses quite strange URLs, such as /?page=2, for browsing the pages of a product listing. I’d prefer that URL simply to be /Page2
So Switch over to Global.asax.cs
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
null, // Route name
"Page{page}", // URL with parameters
new { controller = "Products", action = "List"} // Parameter defaults
);
}
Then you can run it
§4.8 Styling It Up
Now let’s pay some attention to graphic design.
Let’s go for a classic two-column layout with a header, as follows:
In terms of ASP.NET master pages and content pages, the header and sidebar will be defined in the master page, while the main body will be a ContentPlaceHolder called MainContent
§4.8.1 Defining Page Layout in the Master Page
You can easily achieve this layout by updating your master page template, /Views/Shared/Site.Master as follows:
<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
<link rel=Stylesheet href="../../Content/Site.css" />
</head>
<body>
<div >
<div class="title">SPORTS STORE</div>
</div>
<div >
Will put something useful here later
</div>
<div >
<asp:ContentPlaceHolder ID="MainContent" runat="server"/>
</div>
</body>
</html>
§4.8.2 Adding CSS Rules
Now change the default css /Content/Site.css , the added part as follows:
/* -- Leave rest as is --*/
BODY { font-family: Cambria, Georgia, "Times New Roman"; margin: 0; }
DIV#header DIV.title, DIV.item H3, DIV.item H4, DIV.pager A {
font: bold 1em "Arial Narrow", "Franklin Gothic Medium", Arial;
}
DIV#header { background-color: #444; border-bottom: 2px solid #111; color: White; }
DIV#header DIV.title { font-size: 2em; padding: .6em; }
DIV#content { border-left: 2px solid gray; margin-left: 9em; padding: 1em; }
DIV#categories { float: left; width: 8em; padding: .3em; }
DIV.item { border-top: 1px dotted gray; padding-top: .7em; margin-bottom: .7em; }
DIV.item:first-child { border-top:none; padding-top: 0; }
DIV.item H3 { font-size: 1.3em; margin: 0 0 .25em 0; }
DIV.item H4 { font-size: 1.1em; margin:.4em 0 0 0; }
DIV.pager { text-align:right; border-top: 2px solid silver; padding: .5em 0 0 0; margin-top: 1em; }
DIV.pager A { font-size: 1.1em; color: #666; text-decoration: none; padding: 0 .4em 0 .4em; }
DIV.pager A:hover { background-color: Silver; }
DIV.pager A.selected { background-color: #353535; color: White; }
DIV#categories A {
font: bold 1.1em "Arial Narrow","Franklin Gothic Medium",Arial; display: block;
text-decoration: none; padding: .6em; color: Black;
border-bottom: 1px solid silver;
}
DIV#categories A.selected { background-color: #666; color: White; }
DIV#categories A:hover { background-color: #CCC; }
DIV#categories A.selected:hover { background-color: #666; }
FORM { margin: 0; padding: 0; }
DIV.item FORM { float:right; }
DIV.item INPUT { color:White; background-color: #333; border: 1px solid black; cursor:pointer; }
H2 { margin-top: 0.3em }
TFOOT TD { border-top: 1px dotted gray; font-weight: bold; }
.actionButtons A {
font: .8em Arial; color: White; margin: 0 .5em 0 .5em;
text-decoration: none; padding: .15em 1.5em .2em 1.5em;
background-color: #353535; border: 1px solid black;
}
DIV#cart { float:right; margin: .8em; color: Silver;
background-color: #555; padding: .5em .5em .5em 1em; }
DIV#cart A { text-decoration: none; padding: .4em 1em .4em 1em; line-height:2.1em;
margin-left: .5em; background-color: #333; color:White; border: 1px solid black;}
And link the site.css with site.Master
<head runat="server">
<title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
<link rel=Stylesheet href="../../Content/Site.css" />
</head>
Now you can see the graphic design.
§4.8.3 Creating a Partial View
In the end ,let me show you another trick to simplify the List.aspx view. You’ll now learn how to create a partial view.create a partial view file at ~/Views/Shared/
and update the code as follows:
<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<SportsStore.Domain.Entities.Product>" %>
<div class="item">
<h3><%= Model.Name %></h3>
<%= Model.Description %>
<h4><%= Model.Price.ToString("c")%></h4>
</div>
Also , you need to update /Views/Products/List.aspx so that it uses your new partial view, passing a product parameter that will become the partial view’s Model:
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<% foreach(var product in Model.Products) { %>
<% Html.RenderPartial("ProductSummary", product); %>
<% } %>
<div class="pager">
<%= Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new {page = x})) %>
</div>
</asp:Content>
Now you can run your Application. and there is no change . That’s a satisfying simplification.
LET US GO TO THE NEXT CHAPTER !!!
|
请发表评论