十四、Razor 和视图
在本章中,我们将介绍:
- 使用 ViewImports 管理视图中的名称空间
- 创建强类型的 Partial 视图
- 配置视图和区域位置
- 在视图中使用依赖注入
- 创建 HTMLHelpers
使用 ViewImports 管理视图中的名称空间
在本菜谱中,您将学习如何在Razor
视图中创建和管理名称空间,方法是创建_ViewImport
视图,在应用视图中添加所有必要的名称空间。
准备
我们创建一个空的 web 应用,其中包含一个控制器和一个带有相应文件夹的index
视图。
怎么做……
TagHelpers 的目标是减少使用 HTML 标记和/或由Razor
解释的属性编写视图。 它们用最接近 HTML 的语法替换 HTMLHelpers,允许集成器更容易地使用 MVC 视图:
- 首先,在
HomeController.cs
中为GET
、POST
添加一个Insert
动作方法,在POST
的Insert
方法上添加一个断点:
- 接下来,让我们为输入添加带有 TagHelper 代码的
Insert
视图,以测试 TagHelper:
-
我们可以看到在输入文本 HTML 元素中添加了
asp-for
TagHelper。 -
让我们测试一下
POST
方法,在输入中添加一个值:
- 我们点击 Send 按钮,看看在
POST
的Insert
方法中发生了什么:
- 输入为空; 现在,让我们纠正它。 添加一个
_ViewImports.cshtml
页面通过右键单击Views
文件夹,并选择添加|新项目| MVC 视图导入页面:
- 接下来,我们将以下代码添加到
_ViewImports.cshtml
中,以允许在Razor
视图中使用 TagHelpers,导入Microsoft.AspNetCore.Mvc.TagHelpers
命名空间:
- 最后,让我们在
Insert
视图中再次测试POST
方法:
- 我们可以看到 TagHelpers 现在在我们的视图中工作。
创建强类型的 Partial 视图
在本教程中,您将了解什么是Partial
视图,以及如何创建强类型视图。
准备
我们将用 ASP 创建一个空的 web 应用.NET Core MVC 启用,将 MVC 依赖添加到项目中:
"Microsoft.AspNetCore.Mvc": "2.0.0"
怎么做……
我们可以利用 c#编译器的强类型 Partial 视图。 c#编译器检查属性的名称和类型。 如果它检测到误用(比如错误的属性名,等等),它将抛出一个异常,并且不会编译项目:
- 首先,让我们创建
Dto
和ViewModel
对象:
public class ProductDto
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class ProductViewModel
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
[Required]
[Range(0.01, double.MaxValue,
ErrorMessage = "Please enter a positive number")]
public decimal Price { get; set; }
}
- 接下来,我们创建控制器用来检索
ProductViewModel
的服务层:
public interface IProductRepository
{
IEnumerable<ProductDto> GetProducts();
}
public class ProductRepository : IProductRepository
{
private List<ProductDto> _products;
public ProductRepository()
{
_products = new List<ProductDto>()
{
new ProductDto { Id = 1, Name = "Laptop" },
new ProductDto { Id = 2, Name = "Phone" },
new ProductDto { Id = 3, Name = "Desktop" }
};
}
public IEnumerable<ProductDto> GetProducts()
{
return _products.AsEnumerable();
}
}
- 接下来,我们配置
Startup.cs
来管理存储库生命周期和自动映射器:
services.AddScoped<IProductRepository, ProductRepository>();
AutoMapper.Mapper.Initialize(mapper =>
{
mapper.CreateMap<Models.ProductDto,
Models.ProductViewModel>();
});
- 现在,让我们创建一个使用服务层的
ProductController
:
public class ProductController : Controller
{
private readonly IProductRepository _productRepository;
public ProductController(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public IActionResult Index()
{
var productsDtoFromRepo = _productRepository.GetProducts();
var model = Mapper.Map
<IEnumerable<ProductViewModel>>
(productsDtoFromRepo);
return View(model);
}
}
- 现在,让我们创建
ProductController
的index
视图,它将包含强类型Partial
视图:
@model List<ProductViewModel>
<br />
@foreach (var product in Model)
{
@Html.Partial("ProductPartial", product) }
-
这里,
for
循环中的代码调用Html.Partial
。ProductPartial
是分部视图的名称。 这个 Partial 视图必须位于Product Views
文件夹或共享文件夹中。 我们可以看到传递的第二个参数是一个product
对象。 -
现在,让我们看看
ProductPartial.cshtml
视图中的Partial
代码:
@model ProductViewModel
<p>Product @Model.Id : @Model.Name</p>
配置视图和区域位置
在本食谱中,您将学习如何更改 ASP 使用的每个约定的视图位置.NET Core MVC。
准备
我们将使用前面的配方并添加一些修改以使用不同的视图位置。
怎么做……
- 首先,让我们创建一个名为
ViewsOld
的新视图文件夹。 在这个文件夹中,我们将从Views
文件夹中复制_ViewImports.cshtml
和_ViewSart
/cshtml
,并剪切Product
views 文件夹粘贴到ViewsOld
文件夹中:
- 接下来,我们创建一个名为
Infrastructure
的文件夹,并创建一个名为OldViewsExpander.cs
的 c#类。 该类将包含以下代码:
public class OldViewsExpander : IViewLocationExpander
{
public IEnumerable<string> ExpandViewLocations
(ViewLocationExpanderContext context,
IEnumerable<string> viewLocations)
{
// rerturn all classic locations
foreach (string location in viewLocations)
{
yield return location;
}
// return new locations
yield return "/ViewsOld/Shared/{0}.cshtml";
yield return "/ViewsOld/{1}/{0}.cshtml";
// now the following locations will be tested
// "/Views/Home"
// "/Views/Product"
// "/Views/Shared"
// "/ViewsOld/Home"
// "/ViewsOld/Product"
// "/ViewsOld/Shared"
}
public void PopulateValues(ViewLocationExpanderContext context)
{}
}
- 为了在应用中包含这个新配置,我们必须将新的
ViewLocationExpanders
添加到ViewLocationExpanders
应用的列表中:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IProductRepository, ProductRepository>();
services.AddMvc();
services.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationExpanders.Clear(); options.ViewLocationExpanders.Add( new OldViewsExpander());
}); }
- 我们还可以通过将模板视图直接添加到
ViewLocationFormats
列表中,而不使用OldViewsExpander
类来修改或添加整个应用的区域位置:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IProductRepository, ProductRepository>();
services.AddMvc();
services.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationFormats.Add( "/ViewsOld/Shared/{0}.cshtml");
options.ViewLocationFormats.Add( "/ViewsOld/{1}/{0}.cshtml");
}); }
- 为了只使用
ViewsOld
文件夹作为整个应用的视图位置,我们必须使用以下代码,在添加新位置之前清除ViewLocationFormats
列表。 在此之前,我们将共享文件夹及其_Layout.cshtml
文件从Views
文件夹复制到OldViews
文件夹,以避免出现错误。 如果我们不移动共享文件夹和_Layout.cshtml
文件,_ViewStart.cshtml
将在共享文件夹中搜索其布局文件,并且不使用_Layout.cshtml
作为其布局:
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IProductRepository, ProductRepository>();
services.AddMvc();
services.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationFormats.Clear(); options.ViewLocationFormats.Add( "/ViewsOld/Shared/{0}.cshtml");
options.ViewLocationFormats.Add( "/ViewsOld/{1}/{0}.cshtml");
}); }
它是如何工作的…
为了修改或添加View
位置文件夹,我们创建了一个继承了IViewLocationExpander
接口的类。
如果我们没有添加以下代码,我们就不需要包含本机Views
文件夹位置; 我们将替换它:
foreach(string location in viewLocations)
{
yield return location;
}
MVC 机制搜索Views
的两个本地文件夹是:
"/Views/{ControllerName}"
"/Views/Shared"
现在,通过下面的代码,我们将添加新的Views
位置:
yield return "/ViewsOld/Shared/{0}.cshtml";
yield return "/ViewsOld/{1}/{0}.cshtml";
我们可以在不使用OldViewsExpander
类的情况下获得相同的结果,只需将以下代码添加到ConfigureServices
方法:
services.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationFormats.Add(
"/ViewsOld/Shared/{0}.cshtml");
options.ViewLocationFormats.Add(
"/ViewsOld/{1}/{0}.cshtml"); });
现在,一个视图可能的文件夹位置可以是:
"/Views/{ControllerName}"
"/Views/Shared"
"/ViewsOld/{ControllerName}"
"/ViewsOld/Shared"
为了完成这项工作,我们需要为遗留区域添加一个新的文件夹模式,如下代码所示:
services.Configure<RazorViewEngineOptions>(options =>
{
options.ViewLocationFormats.Add(
"/ViewsOld/Shared/{0}.cshtml");
options.ViewLocationFormats.Add(
"/ViewsOld/{1}/{0}.cshtml");
options.AreaViewLocationFormats.Add(
"/AreasOld/{2}/Views/{1}/{0}.cshtml");
options.AreaViewLocationFormats.Add(
"/AreasOld/{2}/Views/Shared/{0}.cshtml"); });
Legend:
// {0} - Action Name
// {1} - Controller Name
// {2} - Area Name
在视图中使用依赖注入
在本教程中,您将了解什么是依赖项注入以及如何在 ASP 中使用它.NET Core MVC 视图。 我们将看到如何注入一个包含类别对象的列表来绑定AddProduct.cshtml
视图中的下拉列表。
依赖注入机制让类通过构造函数参数获得对象,而不是调用工厂方法,如下代码:
public ClassA (ClassB _object) {
this.object = _object;
}
使用下面的代码代替前面的代码:
public ClassA() {
this.object = FactoryClass.GetObjectB();
}
准备
我们将用 ASP 创建一个空的 web 应用.NET Core MVC 通过添加 MVC 依赖到项目中启用:
"Microsoft.AspNetCore.Mvc": "2.0.0"
怎么做……
- 首先,让我们创建
Dto
和ViewModel
对象:
public class ProductDto
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class ProductViewModel
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
[Required]
[Range(0.01, double.MaxValue,
ErrorMessage = "Please enter a positive number")]
public decimal Price { get; set; }
[Required]
public string CategoryName
{
get; set;
} }
- 接下来,我们创建控制器用于检索
ProductViewModel
的服务层。 产品控制器将使用这个存储库。 这个存储库使用一个静态的、硬编码的数据存储,使数据在 HTTP 请求之间持久:
public class ProductDataStore
{
public static ProductDataStore Current { get; }
= new ProductDataStore();
public List<ProductDto> Products { get; set; }
public ProductDataStore()
{
Products = new List<ProductDto>()
{
new ProductDto { Id = 1, Name = "Laptop" },
new ProductDto { Id = 2, Name = "Phone" },
new ProductDto { Id = 3, Name = "Desktop" }
};
}
}
public interface IProductRepository
{
IEnumerable<ProductDto> GetProducts();
}
public class ProductRepository : IProductRepository
{
public List<ProductDto> Products
{
get
{
return ProductDataStore.Current.Products;
}
}
public IEnumerable<ProductDto> GetProducts()
{
return Products.AsEnumerable();
}
}
- 为了在视图中注入数据,我们将使用另一个存储库,它从 JSON 文件中读取数据。 首先,我们将使用
No schema selected
在应用根目录下创建一个 JSON 文件。CategoryJSONDataStore.json
如下所示:
{
"Categories": [
"Computers",
"Software",
"Electronics",
"TV",
"DVD"] }
- 然后,我们创建
CategoryRepository
,它使用这个 JSON 文件并将其发送给视图。 显然,这些数据应该来自一个数据库:
public interface ICategoryRepository
{
List<SelectListItem> GetCategories();
}
public class CategoryRepository: ICategoryRepository
{
private CategoryFormData _data;
public CategoryRepository()
{
_data = JSONConvert
.DeserializeObject<CategoryFormData>(
File
.ReadAllText("CategoryJSONDataStore.json"));
}
public List<SelectListItem> GetCategories()
{
return _data.Categories.Select
(x => new SelectListItem() { Text = x }).ToList();
}
}
public class CategoryFormData
{
public List<string> Categories
{
get; set;
}
}
- 接下来,我们配置
Startup.cs
来管理存储库生命周期,而AutoMapper
:- 在
ConfigureServices
方法中:
- 在
services.AddScoped<IProductRepository,
ProductRepository>();
services.AddSingleton<ICategoryRepository,
CategoryRepository>();
AutoMapper.Mapper.Initialize(mapper => {
mapper.CreateMap<Models.ProductDto,
Models.ProductViewModel>();
mapper.CreateMap<Models.ProductViewModel,
Models.ProductDto>();
});
- 现在,让我们创建一个使用服务层的
ProductController
:
public class ProductController : Controller
{
private readonly IProductRepository
_productRepository;
public ProductController
(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public IActionResult Index()
{
var productsDtoFromRepo =
_productRepository.GetProducts();
var model =
Mapper.Map<IEnumerable<ProductViewModel>>
(productsDtoFromRepo);
return View(model);
}
[HttpGet]
public IActionResult AddProduct()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult AddProduct(ProductViewModel model)
{
if (ModelState.IsValid)
{
model.Id = _productRepository.GetProducts()
.Max(p => p.Id) + 1;
var productDto = Mapper.Map<ProductDto>(model);
_productRepository.AddProduct(productDto);
return RedirectToAction("Index");
}
return View(model);
}
}
- 让我们添加
Index
和AddProduct
动作的视图:Index.cshtml
:
@model List<ProductViewModel>
<br />
@foreach (var p in Model)
{
<p>Product @p.Id : @p.Name</p>
}
@model ProductViewModel
@inject
ICategoryDataStore
Categories <br />
<p>Let's add a new product</p>
@using (Html.BeginForm("AddProduct", "Product", FormMethod.Post))
{
<div class="row">
<div class="col-md-6">
@Html.ValidationSummary(false)
<div class="form-group">
@Html.LabelFor(x => x.Name, "Product Name")
@Html.TextBoxFor(x => x.Name,
new { @class = "form-control" })
@Html.ValidationMessageFor(x => x.Name)
</div>
<div class="form-group">
@Html.LabelFor(x => x.Price, "Product Price")
@Html.TextBoxFor(x => x.Price,
new { @class = "form-control" })
@Html.ValidationMessageFor(x => x.Price)
</div>
<div class="form-group">
@Html.LabelFor(x => x.CategoryName, "Category")
@Html.DropDownListFor(x => x.CategoryName,
Categories.GetCategories(), "Please select one category",
new { @class = "form-control"})
</div>
<div>
<input type="submit" value="Add" />
</div>
</div>
</div>
@section scripts{
<script src="~/lib/jquery-validation/dist/jquery.validate.js">
</script>
<script src="~/lib/jquery-validation-
unobtrusive/jquery.validate.unobtrusive.js">
</script>
}
- 在
Views
文件夹中,我们必须修改_Layout.cshtml
文件和_ViewImports.cshtml
文件中的一些代码,以使视图正常工作:In Shared
/_Layout.cshtml
:
<ul class="nav navbar-nav"><li>
<aasp-area="" asp-controller="Home" asp-action="Index">Home</a>
</li>
<li>
<aasp-area="" asp-controller="Product" asp-action="AddProduct">Add Product</a>
</li>
</ul>
@using R4
@using R4.Models
@using R4.Services @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
- 如果我们启动应用,我们现在可以看到类别下拉列表是通过将
CategoryRepository
直接注入AddProduct
视图而独立于ProductController
绑定的:
创建 HTMLHelpers
在这个食谱中,您将学习如何创建我们自己的 HTMLHelpers。 下面的代码基于前面的配方,在视图中使用依赖注入。
准备
我们将用 ASP 创建一个空的 web 应用.NET Core MVC 通过添加 MVC 依赖到项目中启用:
"Microsoft.AspNetCore.Mvc": "2.0.0".
怎么做……
现在我们可以深入了解视图中的依赖注入:
- 首先,我们将带有相应视图和关联存储库的
AddProduct
操作方法添加到这个模板 web 项目中。 在本例中,产品列表和类别列表来自硬编码代码,但在现实世界中,它应该来自数据库(关系或非关系)、服务或任何其他数据源:- 下面的代码是
Dto
和ViewModel
类:
- 下面的代码是
public class ProductDto
{
public int Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
}
public class ProductViewModel
{
public int Id { get; set; }
[Required]
[MaxLength(50)]
public string Name { get; set; }
[Required]
[Range(0.01, double.MaxValue,
ErrorMessage = "Please enter a positive number")]
public decimal Price { get; set; }
[Required]
public string CategoryName
{
get; set;
} }
public class ProductDataStore
{
public static ProductDataStore Current { get; }
= new ProductDataStore();
public List<ProductDto> Products { get; set; }
public ProductDataStore()
{
Products = new List<ProductDto>()
{
new ProductDto { Id = 1, Name = "Laptop" },
new ProductDto { Id = 2, Name = "Phone" },
new ProductDto { Id = 3, Name = "Desktop" }
};
}
}
public interface IProductRepository
{
IEnumerable<ProductDto> GetProducts();
void AddProduct(ProductDto productDto);
}
public class ProductRepository : IProductRepository
{
public List<ProductDto> Products
{
get
{
return ProductDataStore.Current.Products;
}
}
public IEnumerable<ProductDto> GetProducts()
{
return Products.AsEnumerable();
}
public void AddProduct(ProductDto productDto)
{
Products.Add(productDto);
}
}
public class HomeController : Controller
{
private IProductRepository _productRepository;
public HomeController(IProductRepository productRepository)
{
_productRepository = productRepository;
}
public IActionResult Index()
{
var productsDtoFromRepo = _productRepository.GetProducts();
var model =
Mapper.Map<IEnumerable<ProductViewModel>>
(productsDtoFromRepo);
return View(model);
}
[HttpGet]
public IActionResult AddProduct()
{
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult AddProduct(ProductViewModel model)
{
if (ModelState.IsValid)
{
model.Id = _productRepository.GetProducts()
.Max(p => p.Id) + 1;
var productDto = Mapper.Map<ProductDto>(model);
_productRepository.AddProduct(productDto);
return RedirectToAction("Index");
}
return View(model);
}
}
@using R5
@using R5.Models
@using R5.Services
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@model ProductViewModel
@inject ICategoryDataStore Categories
<br />
<p>Let's add a new product</p>
@using (Html.BeginForm("AddProduct", "Product", FormMethod.Post))
{
<div class="row">
<div class="col-md-6">
@Html.ValidationSummary(false)
<div class="form-group">
@Html.LabelFor(x => x.Name, "Product Name")
@Html.TextBoxFor(x => x.Name,
new { @class = "form-control" })
@Html.ValidationMessageFor(x => x.Name)
</div>
<div class="form-group">
@Html.LabelFor(x => x.Price, "Product Price")
@Html.TextBoxFor(x => x.Price,
new { @class = "form-control" })
@Html.ValidationMessageFor(x => x.Price)
</div>
<div class="form-group">
@Html.LabelFor(x => x.CategoryName, "Category")
@Html.DropDownListFor(x => x.CategoryName,
Categories.GetCategories(), "Please select one category",
new { @class = "form-control"})
@Html.ValidationMessageFor(x => x.CategoryName)
</div>
<div>
<input type="submit" value="Add" />
</div>
</div>
</div>
}
@section scripts
{
<script src="~/lib/jquery-validation/dist/jquery.validate.js">
</script>
<script src="~/lib/jquery-validation-
unobtrusive/jquery.validate.unobtrusive.js">
</script>
}
- 接下来,我们配置
Startup.cs
来管理存储库的生命周期,配置AutoMapper
来实现这一点:- 在
ConfigureServices
方法中:
- 在
services.AddScoped<IProductRepository, ProductRepository>();
services.AddSingleton<ICategoryRepository, CategoryRepository>();
AutoMapper.Mapper.Initialize(mapper => {
mapper.CreateMap<Models.ProductDto, Models.ProductViewModel>();
mapper.CreateMap<Models.ProductViewModel, Models.ProductDto>();
});
- 现在,我们可以详细查看表单中的 Razor
AddProduct.cshtml
代码。 参见以下代码:
<div class="form-group">
@Html.LabelFor(x => x.Name, "Product Name")
@Html.TextBoxFor(x => x.Name, new { @class = "form-control" })
@Html.ValidationMessageFor(x => x.Name)
</div>
- 我们想用下面的代码替换前面的代码:
<div class="form-group">
@Html.CustomTextboxFor(x => x.Name, "Product Name")
</div>
- 要创建这个
CustomTextboxFor``HTMLHelper
,我们必须创建一个静态类,其中包含一个名为CustomTextboxFor
的静态扩展方法。 类名称不重要,但是类名称空间必须是应用的名称空间,以便在应用的任何地方使用:
public static class CustomHTMLHelpers
{
public static IHtmlContent CustomTextboxFor<TModel, TResult>
(this IHtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TResult>> expression,
string labelText)
{
var label = htmlHelper.LabelFor(expression, labelText);
var textBox = htmlHelper.TextBoxFor(expression,
new { @class = "form-control" });
var validation = htmlHelper.ValidationMessageFor
(expression, null,
new { @class = "text-danger" });
var writer = new StringWriter();
label.WriteTo(writer, HtmlEncoder.Default);
textBox.WriteTo(writer, HtmlEncoder.Default);
validation.WriteTo(writer, HtmlEncoder.Default);
return new HtmlString(writer.GetStringBuilder().ToString());
}
}
- 现在我们将表单中的代码更改为下面的代码,我们可以看到它工作了:
@using (Html.BeginForm("AddProduct", "Product", FormMethod.Post))
{
<div class="row">
<div class="col-md-6">
@Html.ValidationSummary(false)
<div class="form-group">
@Html.CustomTextboxFor(x => x.Name, "Product Name")
</div>
<div class="form-group">
@Html.CustomTextboxFor(x => x.Price, "Product Price")
</div>
<div class="form-group">
@Html.LabelFor(x => x.CategoryName, "Category")
@Html.DropDownListFor(x => x.CategoryName,
Categories.GetCategories(), "Please select one category",
new { @class = "form-control"})
@Html.ValidationMessageFor(x => x.CategoryName)
</div>
<div>
<input type="submit" value="Add" />
</div>
</div>
</div>
}
- 下一个任务是,用新创建的 HTMLHelper 用法替换通用的下拉列表代码:
<div class="form-group">
@Html.LabelFor(x => x.CategoryName, "Category")
@Html.DropDownListFor(x => x.CategoryName,
Categories.GetCategories(), "Please select one category",
new { @class = "form-control"})
@Html.ValidationMessageFor(x => x.CategoryName)
</div>
- 用下面的代码替换前面的代码:
<div class="form-group">
@Html.CustomDropDownListFor(x => x.CategoryName,
Categories.GetCategories(), "Category")
</div>
- 为此,我们将以下扩展方法添加到
CustomHTMLHelpers
类中,命名为CustomDropDownListFor
:
public static IHtmlContent CustomDropDownListFor<TModel, TResult>
(this IHtmlHelper<TModel> htmlHelper,
Expression<Func<TModel, TResult>> expression,
IEnumerable<SelectListItem> selectList, string labelText)
{
var label = htmlHelper.LabelFor(expression, labelText);
var dropdownlist = htmlHelper.DropDownListFor
(expression, selectList,
"Please select one element on the list",
new { @class = "form-control" });
var validation = htmlHelper.ValidationMessageFor(expression, null,
new { @class = "text-danger" });
var writer = new StringWriter();
label.WriteTo(writer, HtmlEncoder.Default);
dropdownlist.WriteTo(writer, HtmlEncoder.Default);
validation.WriteTo(writer, HtmlEncoder.Default);
return new HtmlString(writer.GetStringBuilder().ToString());
}
- 现在表单的代码是这样的; 我们可以看到,它不那么冗长:
@using (Html.BeginForm("AddProduct", "Product", FormMethod.Post))
{
<div class="row">
<div class="col-md-6">
@Html.ValidationSummary(false)
<div class="form-group">
@Html.CustomTextboxFor(x => x.Name, "Product Name")
</div>
<div class="form-group">
@Html.CustomTextboxFor(x => x.Price, "Product Price")
</div>
<div class="form-group">
@Html.CustomDropDownListFor
(x => x.CategoryName, Categories.GetCategories(), "Category")
</div>
<div>
<input type="submit" value="Add" />
</div>
</div>
</div>
}
它是如何工作的…
让我们详细看看静态 HTMLHelpersextension
方法。 它们总是返回一个IHtmlContent
(一个以StringWriter
为参数的HtmlString
对象)。 第一个参数是IHtmlHelper<TModel>
,用于将此方法作为extension
方法附加到HtmlHelper``static
类。 之后,我们使用HtmlHelper
创建我们想要生成的所有元素(在当前的例子中,是一个label
、一个textbox
和一个model
属性的验证消息)。 我们将这些元素作为生成的字符串添加到返回的StringWriter
的 HTML 编码格式上。
版权属于:月萌API www.moonapi.com,转载请注明出处