WHY NOT JUST USE A GRIDVIEW?

Một phần của tài liệu Giáo trình lập trình ASP.NET Apress pro ASP NET MVC3 framework pre release (Trang 163 - 173)

If you’ve worked with ASP.NET before, you might think that was a lot of work for a pretty unimpressive result. It has taken us pages and pages just to get a page list. If we were using Web Forms, we could have done the same thing using the ASP.NET Web Forms GridView control, right out of the box, by hooking it up directly to our Products database table.

What we have accomplished so far doesn’t look like much, but it is very different from dragging a GridView onto a design surface. First, we are building an application with a sound and

maintainable architecture that involves proper separation of concerns. Unlike the simplest use of GridView, we have not directly coupled the UI and the database together—an approach that gives quick results but that causes pain and misery over time. Second, we have been creating unit tests as we go, and these allow us to validate the behavior of our application in a natural way that’s nearly impossible with a Web Forms GridView control.

Finally, bear in mind that a lot of this chapter has been given over to creating the underlying infrastructure on which the application is built. We need to define and implement the repository only once, for example, and now that we have, we’ll be able to build and test new features quickly and easily, as the following chapters will demonstrate.

Improving the URLs

We have the page links working, but they still use the query string to pass page information to the server, like this:

http://localhost/?page=2

We can do better, specifically by creating a scheme that follows the pattern of composable URLs. A composable URL is one that makes sense to the user, like this one:

http://localhost/Page2

Fortunately, MVC makes it very easy to change the URL scheme because it uses the ASP.NET routing feature.

All we need to do is add a new route to the RegisterRoutes method in Global.asax.cs, as shown in Listing 7-23.

23. Listing 7-23. Adding a New Route

public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

routes.MapRoute(

null, // we don't need to specify a name "Page{page}",

new { Controller = "Product", action = "List" } );

routes.MapRoute(

"Default", // Route name

"{controller}/{action}/{id}", // URL with parameters

new { controller = "Product", action = "List", id = UrlParameter.Optional } );

}

It is important that you add this route before the Default one. As you’ll see in Chapter 11, routes are processed in the order they are listed, and we need our new route to take precedence over the existing one.

This is the only alteration we need to make to change the URL scheme for our product pagination. The MVC Framework is tightly integrated with the routing function, and so a change like this is automatically reflected in the result produced by the Url.Action method (which is what we use in the List.cshtml view to generate our page links).

Don’t worry if routing doesn’t make sense to you at the moment—we’ll explain it in detail in Chapter 11. If you run the application and navigate to a page, you’ll see the new URL scheme in action, as illustrated in Figure 7-18.

Figure 7-18. The new URL scheme displayed in the browser

Styling the Content

We’ve built a great deal of infrastructure, and our application is really starting to come together, but we have not paid any attention to its appearance. Even though this book isn’t about web design or CSS, the SportStore application design is so miserably plain that it undermines its technical strengths. In this section, we’ll put some of that right.

Note In this part of the chapter, we will ask you to add CSS styles without explaining their meaning. If you want to learn more about CSS, we recommend Pro CSS and HTML Design Patterns by Michael Bowers (Apress, 2007) and Beginning HTML with CSS and HTML by David Schultz and Craig Cook (Apress, 2007).

We are going to implement a classic two-column layout with a header, as shown in Figure 7-19.

Figure 7-19. The design goal for the SportsStore application

Defining Common Content in the Layout

The Razor layout system is the equivalent of the ASPX master page system. We can define content in one place, and then selectively apply it to individual views to create a consistent appearance in our application. We explained how Razor layouts work and are applied in Chapter 5. When we created the List.cshtml view for the Product controller, we asked you to check the option to use a layout, but leave the box that specifies a layout blank. This has the effect of using the default layout, _Layout.cshtml, which can be found in the Views/Shared folder of the SportsStore.WebUI project. Open this file and apply the changes shown in Listing 7-24.

24. Listing 7-24. Modifying the Default Razor Layout

<!DOCTYPE html>

<html>

<head>

<title>@ViewBag.Title</title>

<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />

<script src="@Url.Content("~/Scripts/jquery-1.4.4.min.js")" type="text/javascript"></script>

</head>

<body>

<div id="header">

<div class="title">SPORTS STORE</div>

</div>

<div id="categories">

Will put something useful here later </div>

<div id="content">

@RenderBody() </div>

</body>

</html>

Adding CSS Rules

The HTML markup in Listing 7-24 is characteristic of an ASP.NET MVC application. It is simple and purely semantic. It describes the content, but says nothing about how it should be laid out on the screen. We will use CSS to tell the browser how the elements we just added should be laid out.

Visual Studio creates a CSS file for us automatically, even when creating an empty project. This Site.css file can be found in the Content folder of the SportsStore.WebUI project. This file is already referenced in the

_Layout.cshtml file, as follows:

<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet" type="text/css" />

Tip Notice that the CSS and JavaScript files that are referenced in Listing 7-24 are done so using the

@Url.Content method. Unlike the ASPX view engine, Razor doesn’t automatically interpret the tilde character (~) as a reference for the root of the application, so we must do this explicitly using the helper method.

Open the Site.css file and add the styles shown in Listing 7-25 to the bottom of the file (don’t remove the existing content in Site.css). You don’t need to type these in by hand. You can download the CSS additions and the rest of the project as part of the code samples that accompany this book.

25. Listing 7-25. Defining CSS

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; }

If you run the application, you’ll see that we have improved the appearance—at least a little, anyway. The changes are shown in Figure 7-20.

Figure 7-20. The design-enhanced SportStore application

Creating a Partial View

As a finishing trick for this chapter, we are going to refactor the application to simplify the List.cshtml view. We are going to create a partial view, which is a fragment of content that is embedded in another view. Partial views are contained within their own files and are reusable across views, which can help reduce duplication, especially if you need to render the same kind of data in several places in your application.

To add the partial view, right-click the /Views/Shared folder in the SportsStore.WebUI project and select Add ọ View from the pop-up menu. Set the name of the view to ProductSummary. We want to display details of a product, so select the Product class from the Model class drop-down menu or type in the qualified class name by hand. Check the Create as a partial view option, as shown in Figure 7-21.

Figure 7-21. Creating a partial view

Click the Add button, and Visual Studio will create a partial view file at Views/Shared/ProductSummary.cshtml.

A partial view is very similar to a regular view, except that when it is rendered, it produces a fragment of HTML, rather than a full HTML document. If you open the ProductSummary view, you’ll see that it contains only the model view directive, which is set to our Product domain model class. Apply the changes shown in Listing 7-26.

26. Listing 7-26. Adding Markup to the ProductSummary Partial View

@model SportsStore.Domain.Entities.Product

<div class="item">

<h3>@Model.Name</h3>

@Model.Description

<h4>@Model.Price.ToString("c")</h4>

</div>

Now we need to update Views/Products/List.cshtml so that it uses the partial view. You can see the change in Listing 7-27.

27. Listing 7-27. Using a Partial View from List.cshtml

@model SportsStore.WebUI.Models.ProductsListViewModel

@{

ViewBag.Title = "Products";

}

@foreach (var p in Model.Products) { Html.RenderPartial("ProductSummary", p);

}

<div class="pager">

@Html.PageLinks(Model.PagingInfo, x => Url.Action("List", new {page = x}))

</div>

We’ve taken the markup that was previously in the foreach loop in the List.cshtml view and moved it to the new partial view. We call the partial view using the Html.RenderPartial helper method. The parameters are the name of the view and the view model object.

Tip The RenderPartial method doesn’t return HTML markup like most other helper methods. Instead, it writes content directly to the response stream, which is why we must call it like a complete line of C#, using a semicolon. This is slightly more efficient than buffering the rendered HTML from the partial view, since it will be written to the response stream anyway. If you prefer a more consistent syntax, you can use the Html.Partial method, which does exactly the same as the RenderPartial method, but returns an HTML fragment and can be used as @Html.Partial("ProductSummary", p).

Switching to a partial view like this is good practice, but it doesn’t change the appearance of the application. If you run it, you’ll see that the display remains as before, as shown in Figure 7-22.

Figure 7-22. Applying a partial view

Summary

In this chapter, we have built most of the core infrastructure for the SportsStore application. It doesn’t have many features that you could demonstrate to a client at this point, but behind the scenes, we have the beginnings of a domain model, with a product repository that is backed by SQL Server and the Entity Framework. We have a single controller, ProductController, that can produce paginated lists of products, and we have set up DI and a clean and friendly URL scheme.

If this chapter felt like a lot of setup for little benefit, then the next chapter will balance the equation. Now that we have the fundamental elements out of the way, we can forge ahead and add all of the customer-facing features:

navigation by category, a shopping cart, and a checkout process.

n n n

SportsStore: Navigation and Cart

In the previous chapter, we set up the core infrastructure of the SportsStore application. Now we will use the infrastructure to add key features to the application, and you’ll start to see how the investment in the basic plumbing pays off. We will be able to add important customer-facing features simply and easily. Along the way, you’ll see some additional features that the MVC Framework provides.

Adding Navigation Controls

The SportsStore application will be a lot more usable if we let customers navigate products by category. We will do this in three parts:

 Enhance the List action model in the ProductController class so that it is able to filter the Product objects in the repository.

 Revisit and enhance our URL scheme and revise our rerouting strategy.

 Create the category list that will go into the sidebar of the site, highlighting the current category and linking to others.

Filtering the Product List

We are going to start by enhancing our view model class, ProductsListViewModel. We need to communicate the current category to the view in order to render our sidebar, and this is as good a place to start as any. Listing 8-1 shows the changes we made.

1. Listing 8-1. Enhancing the ProductsListViewModel Class using System.Collections.Generic;

using SportsStore.Domain.Entities;

namespace SportsStore.WebUI.Models {

public IEnumerable<Product> Products { get; set; } public PagingInfo PagingInfo { get; set; }

public string CurrentCategory { get; set; } }

}

We added a new property called CurrentCategory. The next step is to update the ProductController class so that the List action method will filter Product objects by category and use the new property we added to the view model to indicate which category has been selected. The changes are shown in Listing 8-2.

2. Listing 8-2. Adding Category Support to the List Action Method public ViewResult List(string category, int page = 1) {

ProductsListViewModel viewModel = new ProductsListViewModel { Products = repository.Products

.Where(p => category == null || p.Category == category) .OrderBy(p => p.ProductID)

.Skip((page - 1) * PageSize) .Take(PageSize),

PagingInfo = new PagingInfo { CurrentPage = page, ItemsPerPage = PageSize,

TotalItems = repository.Products.Count() },

CurrentCategory = category };

return View(viewModel);

}

We’ve made three changes to this method. First, we added a new parameter called category. This category is used by the second change, which is an enhancement to the LINQ query—if category isn’t null, only those Product objects with a matching Category property are selected. The last change is to set the value of the CurrentCategory property we added to the ProductsListViewModel class. However, these changes mean that the value of TotalItems is incorrectly calculated—we’ll fix this in a while.

Một phần của tài liệu Giáo trình lập trình ASP.NET Apress pro ASP NET MVC3 framework pre release (Trang 163 - 173)

Tải bản đầy đủ (PDF)

(603 trang)