能不能不用CHtml:form submit 重复提交Button提交form

114网址导航Yii实现多按钮保存与提交(不冲突)-Php框架模板-Php教程-壹聚教程网Yii实现多按钮保存与提交(不冲突)
关于Yii实现多按钮保存与提交(不冲突)问题很多初学都不知道如何解决,下面我来给大家整理一个例子。
Yii中只有CForm才可以使用submitted() 方法 ,通过if($form-&submitted('submit'))来判断是不是点击了buttonName为submit的按钮,比如:
'buttons'=&array(
&&&&&&&&'preview'=&array(
&&&&&&&&&&&&'type'=&'submit',
&&&&&&&&&&&&'label'=&yii::t('core','Show&preview'),
&&&&&&&&),
&&&&&&&&'draft'=&array(
&&&&&&&&&&&&'type'=&'submit',
&&&&&&&&&&&&'label'=&yii::t('core','Save&draft'),
&&&&&&&&),
&&&&&&&&'submit'=&array(
&&&&&&&&&&&&'type'=&'submit',
&&&&&&&&&&&&'label'=&yii::t('core','Submit'),
&&&&&&&&),
&&&&&&&&CHtml::link(yii::t('core','Cancel'),yii::app()-&homeUrl),
if($form-&submitted('submit'))
&&&&&&&&&&&&&&&&$model-&status=Post::STATUS_PROPOSED;
&&&&&&&&&&&&else
&&&&&&&&&&&&&&&&$model-&status=Post::STATUS_DRAFT;
但是CActiveForm没有这个方法,一个解决方案是采用古典的html写法:
&input&type=&submit&&name=&submityes&&value&=&&?php&echo&Yii::t('common',&&Submit&)?&&
if(isset($_POST['submityes']))
上一页: &&&&&下一页:相关内容Chapter 6 – Understanding HTML Helpers
This is a rough draft of a chapter from the book ASP.NET MVC Framework Unleashed by Stephen Walther. Comments are welcome and appreciated. When the book is published, the text from this blog entry will be removed and only the code listings will remain.
You use HTML helpers in a view to render HTML content. An HTML helper, in most cases, is just a method that returns a string.
You can build an entire ASP.NET MVC application without using a single HTML helper. However, HTML helpers make your life as a developer easier. By taking advantage of helpers, you can build your views with far less work.
In this chapter, you learn about the standard HTML helpers included with the ASP.NET MVC framework. You learn how to use the standard helpers to render HTML links and HTML form elements.
You also learn how to create custom helpers. We discuss the utility classes included in the ASP.NET framework that make it easier to build custom helpers. You learn how to work with the TagBuilder and the HtmlTextWriter classes.
Next, we tackle building a more complicated HTML helper: we build a DataGrid helper. The DataGrid helper enables you to easily display database records in an HTML table. It also supports paging and sorting.
Finally, we end this chapter with a discussion of how you can build unit tests for your custom HTML helpers.
*** Begin Note ***
In the ASP.NET MVC world, HTML helpers are the equivalent of ASP.NET Web Form controls. Like a web form control, an HTML helper enables you to encapsulate the rendering of HTML. However, unlike a Web Form control, HTML helpers are extremely lightweight. For example, an HTML helper does not have an event model and does not use view state.
*** End Note ***
Using the Standard HTML Helpers
The ASP.NET MVC framework includes a standard set of helpers that you can use to render the most common types of HTML elements. For example, you can use the standard set of helpers to render HTML links and HTML textboxes.
Rendering Links
The easiest way to render an HTML link in a view is to use the HTML.ActionLink() helper. The Html.ActionLink() does not link to a view. Instead, you use the Html.ActionLink() helper to create a link to a controller action.
For example, the view in Listing 1 includes a link to an action named About (see Figure 1).
Figure 1 – Link rendered by Html.ActionLink() helper
Listing 1 – ViewsHomeAbout.aspx
&%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %&
&asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server"&
To learn more about this website, click the following link:
&%= Html.ActionLink("About this Website", "About" ) %&
&/asp:Content&
In Listing 1, the first parameter passed to the Html.ActionLink() represents the link text and the second parameter represents the name of the controller action. This Html.ActionLink() helper renders the following HTML:
&a href=”/Home/About”&About this Website&/a&
The Html.ActionLink() helper has several overloads and supports several parameters:
· linkText – The label for the link.
· actionName – The action that is the target of the link.
· routeValues – The set of values passed to the action.
· controllerName – The controller that is the target of the link.
· htmlAttributes – The set of HTML attributes to add to the link.
· protocol – The protocol for the link (for example, https)
· hostname – The host name for the link (for example, )
· fragment – The fragment (anchor target) for the link. For example, to link to a div in a view with an id of news, you would specify news for the fragment.
Notice that you can pass route values from an Html.ActionLink() to a controller action. For example, you might need to pass the Id of a database record that you want to edit. Here’s how you pass an Id parameter to the Edit() action:
&%= Html.ActionLink(“Edit Record”, “Edit”, new {Id=3})
&%= Html.ActionLink(“Edit Record”, “Edit”, New With {.Id=3})%&
When this Html.ActionLink() is rendered to the browser, the following link is created:
&a href=”/Home/Edit/3″&Edit Record&/a&
*** Begin Note ***
Route values are URL encoded automatically. For example, the string “Hello World!” is encoded to “Hello%20World!”.
*** End Note ***
Rendering Image Links
Unfortunately, you can’t use the Html.ActionLink() helper to render an image link. Because the Html.ActionLink() helper HTML encodes its link text automatically, you cannot pass an &img& tag to this method and expect the tag to render as an image.
Instead, you need to use the Url.Action() helper to generate the proper link. Here’s how you can generate a delete link with an image:
&a href=”&%= Url.Action(“Delete”) %&”&&img src=”../../Content/Delete.png” alt=”Delete” style=”border:0px” /&&/a&
The Url.Action() helper supports a set of parameters that are very similar to those supported by the Html.ActionLink() helper.
Rendering Form Elements
There are several HTML helpers that you can use to render HTML form elements:
· BeginForm()
· CheckBox()
· DropDownList()
· EndForm()
· Hidden()
· ListBox()
· Password()
· RadioButton()
· TextArea()
· TextBox()
The view in Listing 2 illustrates how you can use several of these HTML helpers. The view renders an HTML page with a simple user registration form (see Figure 2).
Listing 2 – ViewsCustomerRegister.aspx [C#]
&%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage&MvcApplication1.Models.Customer&" %&
&asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"&
&%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %&
&% using (Html.BeginForm()) {%&
&fieldset&
&legend&Register&/legend&
&label for="FirstName"&First Name:&/label&
&%= Html.TextBox("FirstName") %&
&%= Html.ValidationMessage("FirstName", "*") %&
&label for="LastName"&Last Name:&/label&
&%= Html.TextBox("LastName") %&
&%= Html.ValidationMessage("LastName", "*") %&
&label for="Password"&Password:&/label&
&%= Html.Password("Password") %&
&%= Html.ValidationMessage("Password", "*") %&
&label for="Password"&Confirm Password:&/label&
&%= Html.Password("ConfirmPassword") %&
&%= Html.ValidationMessage("ConfirmPassword", "*") %&
&label for="Profile"&Profile:&/label&
&%= Html.TextArea("Profile", new {cols=60, rows=10})%&
&%= Html.CheckBox("ReceiveNewsletter") %&
&label for="ReceiveNewsletter" style="display:inline"&Receive Newsletter?&/label&
&input type="submit" value="Register" /&
&/fieldset&
&/asp:Content&
Listing 2 – ViewsCustomerRegister.aspx [VB]
&%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage(Of MvcApplication1.MvcApplication1.Models.Customer)" %&
&asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"&
&%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %&
&% Using Html.BeginForm() %&
&fieldset&
&legend&Register&/legend&
&label for="FirstName"&First Name:&/label&
&%= Html.TextBox("FirstName") %&
&%= Html.ValidationMessage("FirstName", "*") %&
&label for="LastName"&Last Name:&/label&
&%= Html.TextBox("LastName") %&
&%= Html.ValidationMessage("LastName", "*") %&
&label for="Password"&Password:&/label&
&%= Html.Password("Password") %&
&%= Html.ValidationMessage("Password", "*") %&
&label for="Password"&Confirm Password:&/label&
&%= Html.Password("ConfirmPassword") %&
&%= Html.ValidationMessage("ConfirmPassword", "*") %&
&label for="Profile"&Profile:&/label&
&%= Html.TextArea("Profile", new With {.cols=60, .rows=10})%&
&%= Html.CheckBox("ReceiveNewsletter") %&
&label for="ReceiveNewsletter" style="display:inline"&Receive Newsletter?&/label&
&input type="submit" value="Register" /&
&/fieldset&
&% End Using %&
&/asp:Content&
It is worth emphasizing, once again, that these form helpers are simply rendering strings. For example, the Html.TextBox() helper renders a string that includes an “&input&” tag. If you prefer, you could create the view in Listing 2 without using any of these helpers.
Figure 2 – The Register page
*** Begin Note ***
You might have noticed that Listing 2 includes validation helpers. We discuss the validation helpers — Html.ValidationMessage() and Html.ValidationSummary — in Chapter 8, Validating Form Data.
*** End Note ***
Rendering a Form
In Listing 2, the opening and closing &form& tags are created with a using statement. The opening and closing tags are created like this:
&% using (Html.BeginForm()) {%&
… Form Contents …
&% Using Html.BeginForm() %&
… Form Contents …
&% End Using %&
The advantage of opening and closing a &form& tag with a using statement is that you won’t accidently forget to close the &form& tag.
However, if you find this syntax confusing or otherwise disagreeable then you don’t need to use a using statement. Instead, you can open the &form& tag with Html.BeginForm() and close the &form& with HTml.EndForm() like this:
&% Html.BeginForm(); %&
… Form Contents …
&% Html.EndForm(); %&
&% Html.BeginForm() %&
… Form Contents …
&% Html.EndForm() %&
By default, the Html.BeginForm() method renders a form that posts back to the same controller action. In other words, if you retrieved the view by invoking the Customer controller Details() action, then the Html.BeginForm() renders a &form& tag that looks like:
&form action="/Customer/Details" method="post"&
If you want to post to another action, or modify any other property of the &form& tag, then you can call the Html.BeginForm() helper with one or more parameters. The Html.BeginForm() helper accepts the following parameters:
· routeValues — The set of values passed to the action.
· actionName – The action that is the target of the form post.
· controllerName – The controller that is the target of the form post.
· method – The HTTP method of the form post. The possible values are restricted to POST and GET (You can’t use other HTTP methods within HTML, you must use JavaScript).
· htmlAttributes – The set of HTML attributes to add to the form.
Rendering a DropDownList
You can use the Html.DropDownList() helper to render a set of database records in an HTML &select& tag. You represent the set of database records with the SelectList class.
For example, the Index() action in Listing 3 creates an instance of the SelectList class that represents all of the customers from the Customers database table. You can pass the following parameters to the constructor for a SelectList when creating a new SelectList:
· items – The items represented by the SelectList.
· dataValueField – The name of the property to associate with each item in the SelectList as the value of the item.
· dataTextField – The name of the property to display for each item in the SelectList as the label of the item.
· selectedValue – The item to select in the SelectList.
In Listing 3, the SelectList is assigned to ViewData with the name CustomerId.
Listing 3 – ControllersCustomerController.cs [C#]
public ActionResult Index()
ViewData["CustomerId"] = new SelectList(_entities.CustomerSet.ToList(), "Id", "LastName");
return View();
Listing 3 – ControllersCustomerController.vb [VB]
Function Index() As ActionResult
ViewData("CustomerId") = New SelectList(_entities.CustomerSet.ToList(), "Id", "LastName")
Return View()
End Function
In a view, you can display a SelectList by calling the Html.DropDownList() helper with the name of the SelectList. The view in Listing 4 displays the CustomerId SelectList (see Figure 3).
Listing 4 – ViewsCustomerIndex.aspx
&%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %&
&asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"&
&%= Html.DropDownList("CustomerId") %&
&/asp:Content&
Figure 3 – Displaying the CustomerList
The view in Listing 4 renders the following HTML &select& tag:
&select id="CustomerId" name="CustomerId"&
&option value="1"&Walther&/option&
&option value="2"&Henderson&/option&
&option value="3"&Smith&/option&
The Html.DropDownList() helper also supports an optionLabel parameter. You can use this parameter to create a default option at the top of the dropdown list (see Figure 4). You add an optionLabel like this:
&%= Html.DropDownList(“CustomerId”, “Select a Customer”) %&
Figure 4 – Displaying an option label
If a user submits a form with the option label selected, then an empty string is submitted to the server.
Encoding HTML Content
You should always HTML encode user submitted content. Otherwise, an evil hacker can initiate a JavaScript injection attack and, potentially, steal sensitive information from your users such as passwords and credit card numbers.
In a JavaScript injection attack, a hacker submits a JavaScript script when completing an HTML form. When the value of the form field is redisplayed, the script steals information from the page and sends the information to the hacker.
For example, because users typically select their own user names, you should always HTML encode user names that you display in a view like this:
&%= Html.Encode(UserName) %&
HTML encoding replaces characters with special meaning in an HTML document with safe characters:
· & renders &
· & renders &
· “ renders &
· & renders &
Imagine that a hacker submits the following script as the value of a form field:
&script&alert(‘Boom!’)&/script&
When this script is HTML encoded, the script no longer executes. The script gets encoded into the harmless string:
&script&alert(‘Boom!’)&/script&
*** Begin Note ***
The ASP.NET MVC framework prevents a hacker from submitting a form that contains suspicious characters automatically through a feature called request validation. We discussed request validation in Chapter 4, Understanding Views.
*** End Note ***
Using Anti-Forgery Tokens
There is a particular type of JavaScript injection attack that is called a Cross-Site Request Forgery (CSRF) attack. In a CSRF attack, a hacker takes advantage of the fact that you are logged into one website to steal or modify your information at another website.
*** Begin Note ***
To learn more about CSRF attacks, see the Wikipedia entry at . The example discussed in this section is based on the example described in the Wikipedia entry.
*** End Note ***
For example, imagine that you have an online bank account. The bank website identifies and authenticates you with a cookie. Now, imagine that you visit a forums website. This forums website enables users to post messages that contain images. An evil hacker has posted an image to the forums that looks like this:
&img src=”/withdraw?amount=9999” /&
Notice that the src attribute of this image tag points to a URL at the bank website.
When you view this message in your browser, $9,999 dollars is withdrawn from your bank account. The hacker is able to withdraw money from your bank account because the bank website uses a browser cookie to identify you. The hacker has hijacked your browser.
If you are creating the bank website, then you can prevent a CSRF attack by using the Html.AntiForgeryToken() helper. For example, the view in Listing 5 uses the Html.AntiForgeryToken() helper.
Listing 5 – ViewsBankWithdraw.aspx [C#]
&%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %&
&asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"&
&h2&Withdraw&/h2&
&% using (Html.BeginForm()) {%&
&%= Html.AntiForgeryToken() %&
&fieldset&
&legend&Fields&/legend&
&label for="Amount"&Amount:&/label&
&%= Html.TextBox("Amount") %&
&input type="submit" value="Withdraw" /&
&/fieldset&
&/asp:Content&
Listing 5 – ViewsBankWithdraw.aspx [VB]
&%@ Page Title="" Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %&
&asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"&
&h2&Withdraw&/h2&
&% Using Html.BeginForm() %&
&%= Html.AntiForgeryToken() %&
&fieldset&
&legend&Fields&/legend&
&label for="Amount"&Amount:&/label&
&%= Html.TextBox("Amount") %&
&input type="submit" value="Withdraw" /&
&/fieldset&
&% End Using %&
&/asp:Content&
This helper creates a hidden input field that represents a cryptographically strong random value. Each time you request the view, you get a different random value in a hidden field that looks like:
name="__RequestVerificationToken"
type="hidden"
value="6tbg3PWU9oAD3bhw6jZwxrYRyWPhKede87K/PFgaw
6MI3huvHgpjlCcPzDzrTkn8" /&
The helper also creates a cookie that represents the random value. The value in the cookie is compared against the value in the hidden form field to determine whether a CSRF attack is being performed.
The Html.AntiForgeryToken() helper accepts the following optional parameters:
· salt – Enables you to add a cryptographic salt to the random value to increase the security of the anti-forgery token..
· domain – The domain associated with the anti-forgery cookie. The cookie is sent only when requests originate from this domain.
· path – The virtual path associated with the anti-forgery cookie. The cookie is sent only when requests originate from this path.
Generating the random value with the Html.AntiForgeryToken() helper is only half the story. To prevent CSRF attacks, you also must add a special attribute to the controller action that accepts the HTML form post. The Withdraw() controller action in Listing 6 is decorated with a ValidateAntiForgeryToken attribute.
Listing 6 – ControllersBankController.cs [C#]
using System.Web.M
namespace MvcApplication1.Controllers
public class BankController : Controller
// GET: /Bank/Withdraw
public ActionResult Withdraw()
return View();
// POST: /Bank/Withdraw
[AcceptVerbs(HttpVerbs.Post)]
[ValidateAntiForgeryToken]
public ActionResult Withdraw(decimal amount)
// Perform withdrawal
return View();
Listing 6 – ControllersBankController.vb [VB]
Public Class BankController
Inherits Controller
' GET: /Bank/Withdraw
Public Function Withdraw() As ActionResult
Return View()
End Function
' POST: /Bank/Withdraw
&AcceptVerbs(HttpVerbs.Post), ValidateAntiForgeryToken& _
Public Function Withdraw(ByVal amount As Decimal) As ActionResult
' Perform withdrawal
Return View()
End Function
The ValidateAntiForgeryToken attribute compares the hidden form field to the cookie. If they don’t match then the attribute throws the exception in Figure 5.
Figure 5 – AntiForgery validation failure
*** Begin Warning ***
Visitors to your website must have cookies enabled or they will get an AntiForgery exception when posting to a controller action that is decorated with the ValidateAntiForgeryToken attribute.
*** End Warning ***
Creating Custom HTML Helpers
The ASP.NET MVC framework ships with a limited number of HTML helpers. The members of the ASP.NET MVC team identified the most common scenarios in which you would need a helper and focused on creating helpers for these scenarios.
Fortunately, creating new HTML helpers is a very easy process. You create a new HTML helper by creating an extension method on the HtmlHelper class. For example, Listing 7 contains a new Html.SubmitButton() helper that renders an HTML form submit button.
Listing 7 – HelpersSubmitButtonHelper.cs [C#]
using System.Web.M
namespace Helpers
public static class SubmitButtonHelper
/// &summary&
/// Renders an HTML form submit button
/// &/summary&
public static string SubmitButton(this HtmlHelper helper, string buttonText)
return String.Format("&input type="submit" value="{0}" /&", buttonText);
Listing 7 – HelpersSubmitButtonHelper.cs [VB]
Public Module SubmitButtonHelper
''' &summary&
''' Renders an HTML form submit button
''' &/summary&
&pilerServices.Extension& _
Function SubmitButton(ByVal helper As HtmlHelper, ByVal buttonText As String) As String
Return String.Format("&input type=""submit"" value=""{0}"" /&", buttonText)
End Function
End Module
Listing 7 contains an extension method named SubmitButton(). The SubmitButton() helper simply returns a string that represents an HTML &input type=”submit” /& tag.
Because the SubmitButton() method extends the HtmlHelper class, this method appears as a method of the HtmlHelper class in Intellisense (see Figure 6).
Figure 6 – Html.SubmitButton() Html helper included in Intellisense
The view in Listing 8 uses the new Html.SubmitButton() helper to render the submit button for a form. Make sure that you import the namespace associated with your helper or the helper won’t appear in Intellisense. The correct namespace is imported in Listing 8 with the help of the &%@ Import %& directive.
*** Begin Note ***
As an alternative to registering a namespace for a particular view with the &%@ Import %& directive, you can register a namespace for an entire application in the system.web.pages.namespaces section of the web configuration (web.config) file.
*** End Note ***
Listing 8 – ViewsCustomerCreate.aspx
&%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage&MvcApplication1.Models.Customer&" %&
&%@ Import Namespace="Helpers" %&
&asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"&
&% using (Html.BeginForm()) {%&
&fieldset&
&legend&Fields&/legend&
&label for="FirstName"&FirstName:&/label&
&%= Html.TextBox("FirstName") %&
&label for="LastName"&LastName:&/label&
&%= Html.TextBox("LastName") %&
&%= Html.SubmitButton("Create Customer") %&
&/fieldset&
&/asp:Content&
*** Begin Note ***
All of the standard HTML helpers, such as the Html.TextBox() helper, are also implemented as extension methods. This means that you can swap the standard set of helpers for a custom set of helpers.
*** End Note ***
Using the TagBuilder Class
The TagBuilder class is a utility class included in the ASP.NET MVC framework that you can use when building HTML helpers. The TagBuilder class, as it name suggests, makes it easier to build HTML tags.
Here’s a list of the methods of the TagBuilder class:
· AddCssClass() – Enables you to add a new class=”” attribute to a tag.
· GenerateId() – Enables you to add an id attribute to a tag. This method automatically replaces periods in the id (by default, periods are replaced by underscores)
· MergeAttribute() – Enables you to add attributes to a tag. There are multiple overloads of this method.
· SetInnerText() – Enables you to set the inner text of the tag. The inner text is HTML encode automatically.
· ToString() – Enables you to render the tag. You can specify whether you want to create a normal tag, a start tag, an end tag, or a self-closing tag.
The TagBuilder class has four important properties:
· Attributes – Represents all of the attributes of the tag.
· IdAttributeDotReplacement – Represents the character used by the GenerateId() method to replace periods (the default is an underscore).
· InnerHTML – Represents the inner contents of the tag. Assigning a string to this property does not HTML encode the string.
· TagName – Represents the name of the tag.
These methods and properties give you all of the basic methods and properties that you need to build up an HTML tag. You don’t really need to use the TagBuilder class. You could use a StringBuilder class instead. However, the TagBuilder class makes your life a little easier.
The helper in Listing 9, the Html.ImageLink() helper, is created with a TagBuilder. The Html.ImageLink() helper renders an image link.
Listing 9 – HelpersImageLinkHelper.cs [C#]
using System.Web.M
using System.Web.R
namespace Helpers
public static class ImageLinkHelper
public static string ImageLink(this HtmlHelper helper, string actionName, string imageUrl, string alternateText)
return ImageLink(helper, actionName, imageUrl, alternateText, null, null, null);
public static string ImageLink(this HtmlHelper helper, string actionName, string imageUrl, string alternateText, object routeValues)
return ImageLink(helper, actionName, imageUrl, alternateText, routeValues, null, null);
public static string ImageLink(this HtmlHelper helper, string actionName, string imageUrl, string alternateText, object routeValues, object linkHtmlAttributes, object imageHtmlAttributes)
var urlHelper = new UrlHelper(helper.ViewContext.RequestContext);
var url = urlHelper.Action(actionName, routeValues);
// Create link
var linkTagBuilder = new TagBuilder("a");
linkTagBuilder.MergeAttribute("href", url);
linkTagBuilder.MergeAttributes(new RouteValueDictionary(linkHtmlAttributes));
// Create image
var imageTagBuilder = new TagBuilder("img");
imageTagBuilder.MergeAttribute("src", urlHelper.Content(imageUrl));
imageTagBuilder.MergeAttribute("alt", urlHelper.Encode(alternateText));
imageTagBuilder.MergeAttributes(new RouteValueDictionary(imageHtmlAttributes));
// Add image to link
linkTagBuilder.InnerHtml = imageTagBuilder.ToString(TagRenderMode.SelfClosing);
return linkTagBuilder.ToString();
Listing 9 – HelpersImageLinkHelper.vb [VB]
Public Module ImageLinkHelper
&pilerServices.Extension& _
Function ImageLink(ByVal helper As HtmlHelper, ByVal actionName As String, ByVal imageUrl As String, ByVal alternateText As String) As String
Return ImageLink(helper, actionName, imageUrl, alternateText, Nothing, Nothing, Nothing)
End Function
&pilerServices.Extension& _
Function ImageLink(ByVal helper As HtmlHelper, ByVal actionName As String, ByVal imageUrl As String, ByVal alternateText As String, ByVal routeValues As Object) As String
Return ImageLink(helper, actionName, imageUrl, alternateText, routeValues, Nothing, Nothing)
End Function
&pilerServices.Extension& _
Function ImageLink(ByVal helper As HtmlHelper, ByVal actionName As String, ByVal imageUrl As String, ByVal alternateText As String, ByVal routeValues As Object, ByVal linkHtmlAttributes As Object, ByVal imageHtmlAttributes As Object) As String
Dim urlHelper = New UrlHelper(helper.ViewContext.RequestContext)
Dim url = urlHelper.Action(actionName, routeValues)
' Create link
Dim linkTagBuilder = New TagBuilder("a")
linkTagBuilder.MergeAttribute("href", url)
linkTagBuilder.MergeAttributes(New RouteValueDictionary(linkHtmlAttributes))
' Create image
Dim imageTagBuilder = New TagBuilder("img")
imageTagBuilder.MergeAttribute("src", urlHelper.Content(imageUrl))
imageTagBuilder.MergeAttribute("alt", urlHelper.Encode(alternateText))
imageTagBuilder.MergeAttributes(New RouteValueDictionary(imageHtmlAttributes))
' Add image to links
linkTagBuilder.InnerHtml = imageTagBuilder.ToString(TagRenderMode.SelfClosing)
Return linkTagBuilder.ToString()
End Function
End Module
The Html.ImageLink() helper in Listing 9 has three overloads. The helper accepts the following parameters:
actionName – The controller action to invoke.
imageUrl – The URL of the image to display.
alternateText – The alt text to display for the image.
routeValues – The set of route values to pass to the controller action.
linkHtmlAttributes – The set of HTML attributes to apply to the link.
imageHtmlAttribute – The set of HTML attributes to apply to the image.
For example, you can render a delete link by calling the Html.ImageLink() helper like this:
&%= Html.ImageLink(“Delete”, “~/Content/Delete.png”, “Delete Account”, new {AccountId=2}, null, new {border=0}) %&
&%= Html.ImageLink(“Delete”, “~/Content/Delete.png”, “Delete Account”, New With {.AccountId=2}, Nothing, New With {.border=0}) %&
Two instances of the TagBuilder class are used in Listing 9. The first TagBuilder is used to build up the &a& link tag. The second TagBuilder is used to build up the &img& image tag.
Notice that an instance of the UrlHelper class is created. Two methods of this class are called. First, the UrlHelper.Action() method is used to generate the link to the controller action.
Second, the UrlHelper.Content() method is used to convert an application relative path into a full relative path. For example, if your application is named MyApplication then the UrlHelper.Content() method would convert the application relative path “~/Content/Delete.png” into the relative path “/MyApplication/Content/Delete.png”.
Using the HtmlTextWriter Class
As an alternative to using the TagBuilder class to build up HTML content in an HTML helper, you can use the HtmlTextWriter class. Like the TagBuilder class, the HtmlTextWriter class has specialized methods for building up a string of HTML.
Here is a list of some of the more interesting methods of the HtmlTextWriter class (this is not a comprehensive list):
· AddAttribute() – Adds an HTML attribute. When RenderBeginTag() is called, this attribute is added to the tag.
· AddStyleAttribute() – Adds a style attribute. When RenderBeginTag() is called, this style attribute is added to the tag.
· RenderBeginTag() – Renders an opening HTML tag to the output stream.
· RenderEndTag() – Closes the last tag opened with RenderBeginTag().
· Write() – Writes text to the output stream.
· WriteLine() – Writes a newline to the output stream (good for keeping your HTML readable when you do a browser View Source).
For example, the HTML helper in Listing 10 uses the HtmlTextWriter to create a bulleted list.
Listing 10 – HelpersBulletedListHelper.cs [C#]
using System.C
using System.IO;
using System.Web.M
using System.Web.UI;
namespace Helpers
public static class BulletedListHelper
public static string BulletedList(this HtmlHelper helper, string name)
var items = helper.ViewData.Eval(name) as IE
if (items == null)
throw new NullReferenceException("Cannot find " + name + " in view data");
var writer = new HtmlTextWriter(new StringWriter());
// Open UL
writer.RenderBeginTag(HtmlTextWriterTag.Ul);
foreach (var item in items)
writer.RenderBeginTag(HtmlTextWriterTag.Li);
writer.Write(helper.Encode(item));
writer.RenderEndTag();
writer.WriteLine();
// Close UL
writer.RenderEndTag();
// Return the HTML string
return writer.InnerWriter.ToString();
Listing 10 – HelpersBulletedListHelper.vb [VB]
Imports System.IO
Public Module BulletedListHelper
&pilerServices.Extension()& _
Function BulletedList(ByVal helper As HtmlHelper, ByVal name As String) As String
Dim items As IEnumerable = helper.ViewData.Eval(name)
If items Is Nothing Then
Throw New NullReferenceException("Cannot find " & name & " in view data")
Dim writer = New HtmlTextWriter(New StringWriter())
writer.RenderBeginTag(HtmlTextWriterTag.Ul)
For Each item In items
writer.RenderBeginTag(HtmlTextWriterTag.Li)
writer.Write(helper.Encode(item))
writer.RenderEndTag()
writer.WriteLine()
' Close UL
writer.RenderEndTag()
' Return the HTML string
Return writer.InnerWriter.ToString()
End Function
End Module
The list of customers is retrieved from view state with the help of the ViewData.Eval() method. If you call ViewData.Eval(“Customers”), the method attempts to retrieve an item from the view data dictionary named Customers. However, if the Customers item cannot be retrieved from the view data dictionary, then the method attempts to retrieve the value of a property with the name Customers from the view data model (the view data dictionary take precedence over the view data model).
The HtmlTextWriter class is used to render the HTML &ul& and &li& tags needed to create the bulleted list. Each item from the items collection is rendered into the list.
You can call the Html.BulletedList() helper in a view like this:
&%= Html.BulletedList(“Customers”) %&
You can add the list of customers to view data with the controller action in Listing 11.
Listing 11 – ControllersCustomerController.cs [C#]
public ActionResult List()
ViewData["Customers"] = from c in _entities.CustomerSet
select c.LastN
return View();
Listing 11 – ControllersCustomerController.vb [VB]
Function List() As ActionResult
ViewData("Customers") = From c In _entities.CustomerSet _
Select c.LastName
Return View()
End Function
When you invoke the List() action, a list of customer last names are added to view state. When you call the Html.BulletedList() helper method, you get the bulleted list displayed in Figure 7.
Figure 7 – Using the Html.BulletedList HTML helper
There are multiple ways that you can build up HTML content within an HTML helper. You can use the TagBuilder class, the HtmlTextWriter class, or even the StringBuilder class. The choice is entirely a matter of preference.
Creating a DataGrid Helper
In this section, we tackle a more complicated HTML helper: we build an Html.DataGrid() helper that renders a list of database records in an HTML table. We start with the basics and then we add sorting and paging to our Html.DataGrid() helper.
The basic Html.DataGrid() helper is contained in Listing 12.
Listing 12 – HelpersDataGridHelperBasic.cs [C#]
using System.Collections.G
using System.IO;
using System.L
using System.Web.M
using System.Web.UI;
namespace Helpers
public static class DataGridHelper
public static string DataGrid&T&(this HtmlHelper helper)
return DataGrid&T&(helper, null, null);
public static string DataGrid&T&(this HtmlHelper helper, object data)
return DataGrid&T&(helper, data, null);
public static string DataGrid&T&(this HtmlHelper helper, object data, string[] columns)
// Get items
var items = (IEnumerable&T&)
if (items == null)
items = (IEnumerable&T&)helper.ViewData.M
// Get column names
if (columns == null)
columns = typeof(T).GetProperties().Select(p =& p.Name).ToArray();
// Create HtmlTextWriter
var writer = new HtmlTextWriter(new StringWriter());
// Open table tag
writer.RenderBeginTag(HtmlTextWriterTag.Table);
// Render table header
writer.RenderBeginTag(HtmlTextWriterTag.Thead);
RenderHeader(helper, writer, columns);
writer.RenderEndTag();
// Render table body
writer.RenderBeginTag(HtmlTextWriterTag.Tbody);
foreach (var item in items)
RenderRow&T&(helper, writer, columns, item);
writer.RenderEndTag();
// Close table tag
writer.RenderEndTag();
// Return the string
return writer.InnerWriter.ToString();
private static void RenderHeader(HtmlHelper helper, HtmlTextWriter writer, string[] columns)
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
foreach (var columnName in columns)
writer.RenderBeginTag(HtmlTextWriterTag.Th);
writer.Write(helper.Encode(columnName));
writer.RenderEndTag();
writer.RenderEndTag();
private static void RenderRow&T&(HtmlHelper helper, HtmlTextWriter writer, string[] columns, T item)
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
foreach (var columnName in columns)
writer.RenderBeginTag(HtmlTextWriterTag.Td);
var value = typeof(T).GetProperty(columnName).GetValue(item, null) ?? String.E
writer.Write(helper.Encode(value.ToString()));
writer.RenderEndTag();
writer.RenderEndTag();
Listing 12 – HelpersDataGridHelperBasic.vb [VB]
Imports System.IO
Public Module DataGridHelper
&pilerServices.Extension()& _
Function DataGrid(Of T)(ByVal helper As HtmlHelper) As String
Return DataGrid(Of T)(helper, Nothing, Nothing)
End Function
&pilerServices.Extension()& _
Function DataGrid(Of T)(ByVal helper As HtmlHelper, data As object) As String
Return DataGrid(Of T)(helper, data, Nothing)
End Function
&pilerServices.Extension()& _
Function DataGrid(Of T)(ByVal helper As HtmlHelper, ByVal data As IEnumerable(Of T), ByVal columns() As String) As String
' Get items
Dim items = CType(data, IEnumerable(Of T))
If items Is Nothing Then
items = CType(helper.ViewData.Model, IEnumerable(Of T))
' Get column names
If columns Is Nothing Then
columns = GetType(T).GetProperties().Select(Function(p) p.Name).ToArray()
' Create HtmlTextWriter
Dim writer = New HtmlTextWriter(New StringWriter())
' Open table tag
writer.RenderBeginTag(HtmlTextWriterTag.Table)
' Render table header
writer.RenderBeginTag(HtmlTextWriterTag.Thead)
RenderHeader(helper, writer, columns)
writer.RenderEndTag()
' Render table body
writer.RenderBeginTag(HtmlTextWriterTag.Tbody)
For Each item In items
RenderRow(Of T)(helper, writer, columns, item)
writer.RenderEndTag()
' Close table tag
writer.RenderEndTag()
' Return the string
Return writer.InnerWriter.ToString()
End Function
Private Sub RenderHeader(ByVal helper As HtmlHelper, ByVal writer As HtmlTextWriter, ByVal columns() As String)
writer.RenderBeginTag(HtmlTextWriterTag.Tr)
For Each columnName In columns
writer.RenderBeginTag(HtmlTextWriterTag.Th)
writer.Write(helper.Encode(columnName))
writer.RenderEndTag()
Next columnName
writer.RenderEndTag()
Private Sub RenderRow(Of T)(ByVal helper As HtmlHelper, ByVal writer As HtmlTextWriter, ByVal columns() As String, ByVal item As T)
writer.RenderBeginTag(HtmlTextWriterTag.Tr)
For Each columnName In columns
writer.RenderBeginTag(HtmlTextWriterTag.Td)
Dim value = GetType(T).GetProperty(columnName).GetValue(item, Nothing)
if IsNothing(value) Then value = String.Empty
writer.Write(helper.Encode(value.ToString()))
writer.RenderEndTag()
Next columnName
writer.RenderEndTag()
End Module
In Listing 12, the Html.DataGrid() helper method has three overloads. All three overloads are generic overloads. You must supply the type of object – for example, Product – that the Html.DataGrid() should render.
Here are some examples of how you can call the html.DataGrid() helper:
&%= Html.DataGrid&Product&()%&
&%= Html.DataGrid&Product&(ViewData[“products”]) %&
&%= Html.DataGrid&Product&(Model, new string[] {“Id”, “Name”})%&
&%= Html.DataGrid(Of Product)()%&
&%= Html.DataGrid(Of Product)(ViewData(“products”)) %&
&%= Html.DataGrid(Of Product)(Model, New String() {“Id”, “Name”})%&
In the first case, the Html.DataGrid() helper renders all of the items in the view data model into an HTML table. All of the public properties of the Product class are rendered in each row of the HTML table.
In the second case, the contents of the products item in view data is rendered into an HTML table. Again all of the public properties of the Public class are rendered.
In the third case, once again, the contents of the view data model are rendered into an HTML table. However, only the Id and Name properties are rendered (see Figure 8).
Figure 8 – Rendering an HTML table with the Html.DataGrid() helper
The Html.DataGrid() helper uses an HtmlTextWriter to render the HTML table &table&, &thead&, &tr&, &tbody&, &th&, and &td& tags. Rendering these tags by taking advantage of the HtmlTextWriter results in cleaner and more readable code then using string concatenation (Please try to avoid string concatenation whenever possible!).
A tiny bit of reflection is used in the DataGrid() helper. First, reflection is used in the second DataGrid() method to retrieve the list of columns to display when no explicit list of columns is supplied to the helper:
columns = typeof(T).GetProperties().Select(p =& p.Name).ToArray();
columns = GetType(T).GetProperties().Select(Function(p) p.Name).ToArray()
Also, reflection is used to retrieve the value of a property to display within the RenderRow() method:
var value = typeof(T).GetProperty(columnName).GetValue(item, null) ?? String.E
Dim value = GetType(T).GetProperty(columnName).GetValue(item, Nothing)
If IsNothing(value) Then value = String.Empty
*** Begin Note ***
Reflection is a .NET framework feature that enables you get information about classes, methods, and properties at runtime. You can even use reflection to dynamically load assemblies and execute methods at runtime.
*** End Note ***
The Html.DataGrid() helper displays any collection of items that implements the IEnumerable&T& interface. For example, the controller action in Listing 13 assigns a set of products to the view data model property. This list of products can be displayed by the Html.DataGrid() helper.
Listing 13 – ControllersProductController.cs [C#]
using System.L
using System.Web.M
using MvcApplication1.M
namespace MvcApplication1.Controllers
public class ProductController : Controller
private ToyStoreDBEntities _entities = new ToyStoreDBEntities();
public ActionResult Index()
return View(_entities.ProductSet.ToList());
Listing 13 – ControllersProductController.vb [VB]
Public Class ProductController
Inherits System.Web.Mvc.Controller
Private _entities As New ToyStoreDBEntities()
Function Index() As ActionResult
Return View(_entities.ProductSet.ToList())
End Function
Adding Sorting to the DataGrid Helper
Let’s make our Html.DataGrid() helper just a little more fancy. In this section, we’ll add sorting support. When you click a column header in the HTML table rendered by the Html.DataGrid() helper, the HTML table is sorted by the selected column (see Figure 9).
Figure 9 – Html.DataGrid() with sorting support
In order to add the sorting support, we need to modify just one method of the existing DataGridHelper class. We need to modify the RenderHeader() method so that it renders links for headers. The modified RenderHeader() method is contained in Listing 14.
Listing 14 – HelpersDataGridHelperSorting.cs [C#]
private static void RenderHeader(HtmlHelper helper, HtmlTextWriter writer, string[] columns)
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
foreach (var columnName in columns)
writer.RenderBeginTag(HtmlTextWriterTag.Th);
var currentAction = (string)helper.ViewContext.RouteData.Values["action"];
var link = helper.ActionLink(columnName, currentAction, new {sort=columnName});
writer.Write(link);
writer.RenderEndTag();
writer.RenderEndTag();
Listing 14 – HelpersDataGridHelperSorting.vb [VB]
Private Sub RenderHeader(ByVal helper As HtmlHelper, ByVal writer As HtmlTextWriter, ByVal columns() As String)
writer.RenderBeginTag(HtmlTextWriterTag.Tr)
For Each columnName In columns
writer.RenderBeginTag(HtmlTextWriterTag.Th)
Dim currentAction = CStr(helper.ViewContext.RouteData.Values("action"))
Dim link = helper.ActionLink(columnName, currentAction, New With {Key .sort = columnName})
writer.Write(link)
writer.RenderEndTag()
Next columnName
writer.RenderEndTag()
The modified RenderHeader() method in Listing 14 creates a link for each header column by calling the HtmlHelper.ActionLink() method. Notice that the name of the header column is included as a route value in the link. For example, the following link is rendered for the Price header:
/Product/SortProducts?sort=Price
The actual database sorting happens within the Product controller. The SortProducts action in Listing 15 returns the products in different sort orders depending on the value of the sort parameter passed to the action.
Listing 15 – ControllersProductController.cs with SortProducts [C#]
public ActionResult SortProducts(string sort)
IEnumerable&Product&
sort = sort ?? string.E
switch (sort.ToLower())
case "name":
products = from p in _entities.ProductSet
orderby p.N
case "price":
products = from p in _entities.ProductSet
orderby p.Price
products = from p in _entities.ProductSet
orderby p.Id
return View(products);
Listing 15 – ControllersProductController.vb with SortProducts [VB]
Function SortProducts(ByVal sort As String) As ActionResult
Dim products As IEnumerable(Of Product)
sort = If((sort && Nothing), sort, String.Empty)
Select Case sort.ToLower()
Case "name"
products = From p In _entities.ProductSet _
Order By p.Name _
Case "price"
products = From p In _entities.ProductSet _
Order By p.Price _
products = From p In _entities.ProductSet _
Order By p.Id _
End Select
Return View(products)
End Function
Adding Paging to the DataGrid Helper
It really wouldn’t be a proper Html.DataGrid() helper unless the helper supported paging. In this section, we modify our Html.DataGrid() helper so that it supports efficient paging through a large set of database records (see Figure 10).
Figure 10 – Paging through database records
In order to add paging support, we need to create two new supporting classes:
· PagedList – An instance of this class is passed to the Html.DataGrid() helper to represent a single page of records.
· PagingLinqExtensions – This class contains extension methods that extend the IQueryable&T& interface with ToPagedList() methods that return a PagedList from a query.
The PagedList class is contained in Listing 16. This class
Listing 16 – PagingPagedList.cs [C#]
using System.Collections.G
namespace Paging
public class PagedList&T& : List&T&
public PagedList(IEnumerable&T& items, int pageIndex, int pageSize, int totalItemCount, string sortExpression)
this.AddRange(items);
this.PageIndex = pageI
this.PageSize = pageS
this.SortExpression = sortE
this.TotalItemCount = totalItemC
this.TotalPageCount = (int)Math.Ceiling(totalItemCount / (double)pageSize);
public int PageIndex { }
public int PageSize { }
public string SortExpression { }
public int TotalItemCount { }
public int TotalPageCount { }
Listing 16 – PagingPagedList.vb [VB]
Public Class PagedList(Of T)
Inherits List(Of T)
Private _pageIndex As Integer
Private _pageSize As Integer
Private _sortExpression As String
Private _totalItemCount As Integer
Private _totalPageCount As Integer
Public Sub New(ByVal items As IEnumerable(Of T), ByVal pageIndex As Integer, ByVal pageSize As Integer, ByVal totalItemCount As Integer, ByVal sortExpression As String)
Me.AddRange(items)
Me.PageIndex = pageIndex
Me.PageSize = pageSize
Me.SortExpression = sortExpression
Me.TotalItemCount = totalItemCount
Me.TotalPageCount = CInt(Fix(Math.Ceiling(totalItemCount / CDbl(pageSize))))
Public Property PageIndex() As Integer
Return _pageIndex
Set(ByVal value As Integer)
_pageIndex = value
End Property
Public Property PageSize() As Integer
Return _pageSize
Set(ByVal value As Integer)
_pageSize = value
End Property
Public Property SortExpression() As String
Return _sortExpression
Set(ByVal value As String)
_sortExpression = value
End Property
Public Property TotalItemCount() As Integer
Return _totalItemCount
Set(ByVal value As Integer)
_totalItemCount = value
End Property
Public Property TotalPageCount() As Integer
Return _totalPageCount
Private Set(ByVal value As Integer)
_totalPageCount = value
End Property
The PagedList class inherits from the base generic List class and adds specialized properties for paging. The PageList class represents the following properties:
· PageIndex – The currently selected page (zero based).
· PageSize – The number of records to display per page.
· SortExpression – The column that determines the sort order of the records.
· TotalItemCount – The total number of items in the database.
· TotalPageCount – The total number of page numbers to display.
The second class, the PagingLinqExtensions class, extends the IQueryable interface to make it easier to return a PagedList from a LINQ query. The PagingLinqExtensions class is contained in Listing 17.
Listing 17 – PagingPagingLinqExtensions.cs [C#]
using System.L
namespace Paging
public static class PageLinqExtensions
public static PagedList&T& ToPagedList&T&
this IQueryable&T& allItems,
int? pageIndex,
int pageSize
return ToPagedList&T&(allItems, pageIndex, pageSize, String.Empty);
public static PagedList&T& ToPagedList&T&
this IQueryable&T& allItems,
int? pageIndex,
int pageSize,
string sort
var truePageIndex = pageIndex ?? 0;
var itemIndex = truePageIndex * pageS
var pageOfItems = allItems.Skip(itemIndex).Take(pageSize);
var totalItemCount = allItems.Count();
return new PagedList&T&(pageOfItems, truePageIndex, pageSize, totalItemCount, sort);
Listing 17 – PagingPagingLinqExtensions.vb [VB]
Public Module PageLinqExtensions
&pilerServices.Extension& _
Function ToPagedList(Of T)(ByVal allItems As IQueryable(Of T), ByVal pageIndex As Integer?, ByVal pageSize As Integer) As PagedList(Of T)
Return ToPagedList(Of T)(allItems, pageIndex, pageSize, String.Empty)
End Function
&pilerServices.Extension& _
Function ToPagedList(Of T)(ByVal allItems As IQueryable(Of T), ByVal pageIndex As Integer?, ByVal pageSize As Integer, ByVal sort As String) As PagedList(Of T)
Dim truePageIndex = If(pageIndex.HasValue, pageIndex, 0)
Dim itemIndex = truePageIndex * pageSize
Dim pageOfItems = allItems.Skip(itemIndex).Take(pageSize)
Dim totalItemCount = allItems.Count()
Return New PagedList(Of T)(pageOfItems, truePageIndex, pageSize, totalItemCount, sort)
End Function
End Module
The PagingLinqExtensions class makes it possible to return a PagedList like this:
var products = _entities.ProductSet
.OrderBy(p =& p.Id)
.ToPagedList(page, 2);
Dim products = _entities.ProductSet _
.OrderBy(Function(p) p.Id) _
.ToPagedList(page, 2)
Notice how you can call ToPagedList() directly on a LINQ query. The PagingLinqExtensions class simplifies your code.
Finally, we need to modify our Html.DataGrid() class to use the PagedList class to represent database records. The modified Html.DataGrid() class includes the new RenderPagerRow() method contained in Listing 18.
Listing 18 – HelpersDataGridHelperPaging.cs [C#]
private static void RenderPagerRow&T&(HtmlHelper helper, HtmlTextWriter writer, PagedList&T& items, int columnCount)
// Don't show paging UI for only 1 page
if (items.TotalPageCount == 1)
// Render page numbers
writer.RenderBeginTag(HtmlTextWriterTag.Tr);
writer.AddAttribute(HtmlTextWriterAttribute.Colspan, columnCount.ToString());
writer.RenderBeginTag(HtmlTextWriterTag.Td);
var currentAction = (string)helper.ViewContext.RouteData.Values["action"];
for (var i = 0; i & items.TotalPageC i++)
if (i == items.PageIndex)
writer.Write(String.Format("&strong&{0}&/strong&&", i + 1));
var linkText = String.Format("{0}", i + 1);
var link = helper.ActionLink(linkText, currentAction, new { page = i, sort=items.SortExpression});
writer.Write(link + "&");
writer.RenderEndTag();
writer.RenderEndTag();
Listing 18 – HelpersDataGridHelperPaging.vb [VB]
Private Sub RenderPagerRow(Of T)(ByVal helper As HtmlHelper, ByVal writer As HtmlTextWriter, ByVal items As PagedList(Of T), ByVal columnCount As Integer)
' Don't show paging UI for only 1 page
If items.TotalPageCount = 1 Then
' Render page numbers
writer.RenderBeginTag(HtmlTextWriterTag.Tr)
writer.AddAttribute(HtmlTextWriterAttribute.Colspan, columnCount.ToString())
writer.RenderBeginTag(HtmlTextWriterTag.Td)
Dim currentAction = CStr(helper.ViewContext.RouteData.Values("action"))
For i = 0 To items.TotalPageCount - 1
If i = items.PageIndex Then
writer.Write(String.Format("&strong&{0}&/strong&&", i + 1))
Dim linkText = String.Format("{0}", i + 1)
Dim link = helper.ActionLink(linkText, currentAction, New With {Key .page = i, Key .sort = items.SortExpression})
writer.Write(link & "&")
writer.RenderEndTag()
writer.RenderEndTag()
The RenderPagerRow() method in Listing 18 renders the user interface for paging. This method simply renders a list of page numbers that act as hyperlinks. The selected page number is highlighted with an HTML &strong& tag.
The modified Html.DataGrid() helper requires an instance of the PagedList class for its data parameter. You can use the controller action in Listing 19 to add the right data to view state.
Listing 19 – ControllersProductController.cs with PagedProducts [C#]
public ActionResult PagedProducts(int? page)
var products = _entities.ProductSet
.OrderBy(p =& p.Id).ToPagedList(page, 2);
return View(products);
Listing 19 – ControllersProductController.vb with PagedProducts [VB]
Function PagedProducts(ByVal page As Integer?) As ActionResult
Dim products = _entities.ProductSet _
.OrderBy(Function(p) p.Id) _
.ToPagedList(page, 2)
Return View(products)
End Function
*** Begin Warning ***
In Listing 19, notice that the ToPagedList() method is called on a LINQ query that includes a call to the OrderBy() method. When using the Entity Framework, you must order the results of a query before you can extract a page of records from the query.
*** End Warning ***
If you want to both page and sort the products, then you can use the controller action in Listing 20.
Listing 20 – ControllersProductController.cs with PagedSortedProducts [C#]
public ActionResult PagedSortedProducts(string sort, int? page)
IQueryable&Product&
sort = sort ?? string.E
switch (sort.ToLower())
case "name":
products = from p in _entities.ProductSet
orderby p.Name
case "price":
products = from p in _entities.ProductSet
orderby p.Price
products = from p in _entities.ProductSet
orderby p.Id
ViewData.Model = products.ToPagedList(page, 2, sort);
return View();
Listing 20 – ControllersProductController.cs with PagedSortedProducts [VB]
Function PagedSortedProducts(ByVal sort As String, ByVal page As Integer?) As ActionResult
Dim products As IQueryable(Of Product)
sort = If((sort && Nothing), sort, String.Empty)
Select Case sort.ToLower()
Case "name"
products = From p In _entities.ProductSet _
Order By p.Name _
Case "price"
products = From p In _entities.ProductSet _
Order By p.Price _
products = From p In _entities.ProductSet _
Order By p.Id _
End Select
ViewData.Model = products.ToPagedList(page, 2, sort)
Return View()
End Function
Notice that when you want to support sorting, you must pass the current sort column to the ToPageList() method. If you don’t pass the current sort column then clicking a page number causes the Html.DataGrid() to forget the sort order.
Testing Helpers
In general, you should place any complicated view logic in an HTML helper. There is a simple reason for this: you can test a helper but you cannot test a view.
The Html.DataGrid() helper that we created in the previous section is a good example of a helper that requires unit tests. There are several things that I could have gotten wrong while writing this helper. You should never trust anything that you write!
Here are some expectations for our helper that we might want to test:
· The helper displays the right number of table rows. For example, if you specify that the page size is 2 rows then calling the Html.DataGrid() helper method should render an HTML table that contains 4 rows (1 header row + 2 data rows + 1 pager row).
· The helper selects the right page number. For example, if you specify that the current page index is 1 then page number 2 should be highlighted in bold in the pager user interface.
The test class in Listing 21 contains unit tests for both of these expectations.
Listing 21 – HelpersDataGridHelperTests.cs [C#]
using System.Collections.G
using System.L
using System.Text.RegularE
using Microsoft.VisualStudio.TestTools.UnitT
using MvcF
namespace MvcApplication1.Tests.Helpers
[TestClass]
public class DataGridHelperTests
public List&Product& CreateItems(int count)
var items = new List&Product&();
for (var i=0;i &i++)
var newProduct = new Product();
newProduct.Id =
newProduct.Name = String.Format("Product {0}", i);
newProduct.Price = count -
items.Add(newProduct);
[TestMethod]
public void SecondPageNumberSelected()
// Arrange
var items = CreateItems(5);
var data = items.AsQueryable().ToPagedList(1, 2);
var fakeHtmlHelper = new FakeHtmlHelper();
var results = DataGridHelper.DataGrid&Product&(fakeHtmlHelper, data);
StringAssert.Contains(results, "&strong&2&/strong&");
[TestMethod]
public void CorrectNumberOfRows()
// Arrange
var items = CreateItems(5);
var data = items.AsQueryable().ToPagedList(1, 2);
var fakeHtmlHelper = new FakeHtmlHelper();
var results = DataGridHelper.DataGrid&Product&(fakeHtmlHelper, data);
// Assert (1 header row + 2 data rows + 1 pager row)
Assert.AreEqual(4, Regex.Matches(results, "&tr&").Count);
public class Product
public int Id { }
public string Name { }
public decimal Price { }
Listing 21 – HelpersDataGridHelperTests.vb [VB]
Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports MvcFakes
Imports System.Text.RegularExpressions
&TestClass()& _
Public Class DataGridHelperTests
Public Function CreateItems(ByVal count As Integer) As List(Of Product)
Dim items = New List(Of Product)()
For i = 0 To count - 1
Dim newProduct = New Product()
newProduct.Id = i
newProduct.Name = String.Format("Product {0}", i)
newProduct.Price = count - i
items.Add(newProduct)
Return items
End Function
&TestMethod()& _
Public Sub SecondPageNumberSelected()
Dim items = CreateItems(5)
Dim data = items.AsQueryable().ToPagedList(1, 2)
Dim fakeHtmlHelper = New FakeHtmlHelper()
Dim results = DataGridHelper.DataGrid(Of Product)(fakeHtmlHelper, data)
StringAssert.Contains(results, "&strong&2&/strong&")
&TestMethod()& _
Public Sub CorrectNumberOfRows()
Dim items = CreateItems(5)
Dim data = items.AsQueryable().ToPagedList(1, 2)
Dim fakeHtmlHelper = New FakeHtmlHelper()
Dim results = DataGridHelper.DataGrid(Of Product)(fakeHtmlHelper, data)
' Assert (1 header row + 2 data rows + 1 pager row)
Assert.AreEqual(4, Regex.Matches(results, "&tr&").Count)
Public Class Product
Private _id As Integer
Private _name As String
Private _price As Decimal
Public Property Id() As Integer
Return _id
Set(ByVal value As Integer)
_id = value
End Property
Public Property Name() As String
Return _name
Set(ByVal value As String)
_name = value
End Property
Public Property Price() As Decimal
Return _price
Set(ByVal value As Decimal)
_price = value
End Property
If I want to feel completely confident about the Html.DataGrid() helper then I would need to write several more unit tests than the two tests contained in Listing 21. However, Listing 21 is a good start.
Both of the unit tests in Listing 21 take advantage of a utility method named CreateItems() that creates a list that contains a specific number of products.
Both unit tests also take advantage of the FakeHtmlHelper class from the MvcFakes project. When you call an HTML helper, you must supply an instance of the HtmlHelper class as the first parameter. The FakeHtmlHelper enables you to easily fake this helper.
*** Begin Note ***
Before you can run the tests in Listing 21, you must add a reference to the MvcFakes project to your Test project. The MvcFakes project is included in the CommonCode folder on the CD that accompanies this book.
*** End Note ***
This chapter was devoted to the topic of HTML helpers. You learned how to create views more easily by using HTML helpers to render HTML content.
In the first part of this chapter, you learned how to use the standard set of HTML helpers included with the ASP.NET MVC framework. For example, you learned how to create HTML links with the Html.ActionLink() and Url.Action() helpers. You also learned how to use the form helpers to render standard HTML form elements such as dropdown lists and textboxes.
We also discussed how you can make your websites more secure against JavaScript injection attacks by taking advantage of the Html.AntiForgeryToken() and Html.Encode() helpers.
In the next part of this chapter, we examined how you can create custom HTML helpers. We talked about the different utility classes that you can use when building a custom helper. In particular, you learned how to use the TagBuilder and HtmlTextWriter classes.
We then tackled building a real-world HTML helper. We created an Html.DataGrid() helper that renders database records in an HTML table. We added both sorting and paging support to our custom Html.DataGrid() helper.
In the final section of this chapter, you learned how to build unit tests for your custom HTML helpers. We created two unit tests for our Html.DataGrid() helper.
Looks good.
I’m reading over it now.
Definitely looking forward to the book after readign all your sample chapters.
Likewise, I am hoping that ASP.NET MVC final release is shipped very very soon.
I sure miss having the official documentation available at my fingertips in the IDE.
Any clues on ETA? ?
If you liked this blog entry then please
Last week I taught the sixth week of the nine-week AngularJS+ASP.NET bootcamp.
The sixth week was all about Agile. We focused on software project management using Scrum and Visual Studio Team Services (formally known as Visual Studio Online). The students learn how to participate on an Agile team. They learn how to create a Product [&]
I taught the fifth week of the FullStack .NET bootcamp last week and the week was devoted to building mobile apps. Students learned how to build hybrid mobile apps that can run on iPhones, Windows phones, and Android phones. We used the following technologies: TACO – The Visual Studio Tools for Apache Cordova. Ionic – [&]
This past week of the bootcamp was devoted to the Microsoft ASP.NET Web API. In other words, students learned how to build the server-side half of their full stack Web apps. By this point in the camp, students understand how all of the various pieces of a modern web app fit together. On Monday, we [&]
This third week of the boot camp was all about learning C#. This means writing a lot of console apps and getting familiar with the standard classes in the .NET framework. In the C# week, students devote a significant amount of time learning how to work with the collection classes such as the List&T& and [&]
Copyright & 2016

我要回帖

更多关于 js form 提交submit 的文章

 

随机推荐