热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

ASP.NETMVCPart.3(自定义视图、强化模型)

之前的示例中,VS为我们自动生成了视图,这个特性很有用,但最终得到的视图太过简单并且需要根据数据模型类型进行裁剪。例如,添加一个产品时,有一个用于用户输入的Prod

       之前的示例中,VS 为我们自动生成了视图,这个特性很有用,但最终得到的视图太过简单并且需要根据数据模型类型进行裁剪。

       例如,添加一个产品时,有一个用于用户输入的 ProductID 值和 Discontinued 值的字段。我们并不希望用户输入这些值,更何况 ProductID 值是表的主键且可以自动生成。我们也不希望用户在一个布尔类型的字段中任意输入值。

       这一部分我们就来演示如何使用 MVC 视图更好的和数据模型约束协作,并使之更好的和整体应用程序相适应。为了掌握 MVC 视图,你必须知道 3 个组件,它们是模型数据视图数据HTML 辅助方法

 

修改视图

       观察 Details.aspx 视图。MVC 视图的页面定义指定了它要使用的数据模型类型。下面是 Details 视图的页面定义,它用于显示 Products 类:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<BasicMvcApplication.Models.Products>" %>

 

       要显示的类型成员可以通过对 Model 的引用获得,在 Details 视图中到处都可以见到它们(每个视图的 Model 都是被页面定义好的类型的实体):

<div class="display-label">ProductID</div>
<div class="display-field"><%: Model.ProductID %></div>
 
<div class="display-label">ProductName</div>
<div class="display-field"><%: Model.ProductName %></div>
 
<div class="display-label">SupplierID</div>
<div class="display-field"><%: Model.SupplierID %></div>

       正是由于这些对 Model 的调用创建了 Details 视图。我们首先要做的事是整理显示。

       我们期望在一个表格中显示产品的细节,而不是只罗列字段名称和值:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<BasicMvcApplication.Models.Products>" %>
 
<asp:Content ID="Content1" COntentPlaceHolderID="TitleContent" runat="server">
    Details
</asp:Content>
 
<asp:Content ID="Content2" COntentPlaceHolderID="MainContent" runat="server">
 
    <h2>Details</h2>
 
    <fieldset>
        <legend>Product Details</legend>
        <table>
            <tr><td>Product Name:</td><td><%: Model.ProductName %></td></tr>
            <tr><td>Supplier ID:</td><td><%: Model.SupplierID %></td></tr>
            <tr><td>Category ID:</td><td><%: Model.CategoryID %></td></tr>
            <tr><td>Quantity per Unit:</td><td><%: Model.QuantityPerUnit %></td></tr>
            <tr><td>Unit Price:</td><td><%: Model.UnitPrice %></td></tr>
            <tr><td>Units in Stock:</td><td><%: Model.UnitsInStock %></td></tr>
            <tr><td>Units on Order:</td><td><%: Model.UnitsOnOrder %></td></tr>
            <tr><td>Recorder Level:</td><td><%: Model.ReorderLevel %></td></tr>
            <tr><td>Discontinued:</td><td><%: Model.Discontinued %></td></tr>
        </table>
    </fieldset>
    <p>
        <%: Html.ActionLink("Edit", "Edit", new { id=Model.ProductID }) %> |
        <%: Html.ActionLink("Back to List", "Index") %>
    </p>
 
</asp:Content>

       我们移除了对主键的引用,为了让数据模型正常工作我们需要它,但并不需要把它显示给用户。重构视图时,只是直接调用 Model.ProductID

 

       下一步是修改某些字段的显示方式。先从 UnitPrice 字段开始(在 MVC 视图里使用标准的 ASP.NET 特性):

<tr><td>Unit Price:</td><td><%: string.Format("{0:F2}", Model.UnitPrice)%></td></tr>

 

       MVC 最棒的一个特性是 HTML 辅助方法,它简化了从模型数据生成 HTML 的过程。我们并不希望把布尔值显示为文本字符,现在做下面这个的修改:

<tr>
    <td>Discontinued:</td>
    <td><%: Html.CheckBoxFor(e => e.Discontinued, new {disabled="true"})%></td>
</tr>

       这里,我们使用了 Html.CheckBoxFor 辅助方法。这个方法接受一个 Lambda 表达式以识别复选框关联的字段,并接受了一个可以用于指定额外 HTML 属性的对象。Lambda 表达式选中的 Discontinued 字段并设定 disabled 属性(因为我们在显示静态的细节)。

       Details 视图被呈现时,对 HTML 辅助方法的调用生成定义复选框的 HTML 定义并设置状态来匹配模型数据

 

 

下表是最常用的 Html 辅助方法:

HTML 方法

描    述

ActionLink 创建调用此 MVC 应用程序控制器方法的链接
BeginForm 创建将回发到控制器方法的表单
CheckBoxFor 创建用于布尔值的复选框
DropDownListFor 使用 SelectList 创建下拉列表
ListBoxFor 创建允许多选的列表
PasswordFor 创建适合输入密码的文本框
RadioButtonFor 创建单选按钮
TextAreaFor 创建多行文本输入区域
TextBoxFor 创建单行的文本输入框

       上表列出的辅助方法都是强类型的辅助方法,它们是在 MVC 2 中引入的。它们接受 Lambda 表达式,用于识别要为之生成的 HTML 的数据字段,如果字段不存在或不能够呈现为请求的 HTML 类型,它们会产生编译时错误。

       辅助方法 ActionLink 用于生成回调 MVC 应用程序的链接,它也非常有用。默认的 Details 视图在页面底部有 2 个链接,这里再给它增加一个调用控制器中 Delete 方法的链接:

<%: Html.ActionLink("Delete", "Delete", new { id=Model.ProductID })%>|

       现在查看详细页面,就可以在不返回 Index 视图下删除产品记录了:

image

 

 

增加视图数据

       Details 视图还是有一些问题。SupplierID 和 CategoryID 字段是访问其他表的关键,但这样的显示对用户毫无帮助。下面将演示如何通过视图数据特性来实现这一功能,它能够让控制器向视图传递模型数据之外的信息

       首先,要给 NorthwindAccessConsolidator 类添加方法,以便能够得到用户友好的类别和供应商名称:

public string GetSupplierName(Products prod)
{
    return db.Suppliers
        .Where(e => e.SupplierID == prod.SupplierID)
        .Select(e => e.CompanyName)
        .Single();
}
 
public string GetCategoryName(Products prod)
{
    return db.Categories
        .Where(e => e.CategoryID == prod.CategoryID)
        .Select(e => e.CategoryName)
        .Single();
}

 

        然后更新控制器以获得类别和供应商、并把它们传递给视图,下面是更新后的 Details 方法:

public ActionResult Details(int id)
{
    Products prod = nwa.GetProduct(id);
    if (prod == null)
    {
        throw new NoSuchRecordException();
    }
    else
    {
        ViewData["CatName"] = nwa.GetCategoryName(prod);
        ViewData["SupName"] = nwa.GetSupplierName(prod);
        return View(prod);
    }
}

       这里要注意的是,通过 ViewData 集合可以向视图传送任意的数据,我们通过继承默认的控制器类获得了它。之后,视图就可以通过 ViewData 集合获取到传递的数据,下面是 Details.aspx 视图修改后的标记:

Supplier ID:<%: ViewData["SupName"] %>
Category ID:<%: ViewData["CatName"]%>

 

       SupplierID 和 CategoryID 字段在数据模型中仍然可用,但我们选择不再使用它们。能够访问模型数据字段并轻松生成 HTML 的能力意味着我们能够方便的裁剪视图。仅通过一点点的努力我们就改进了 Details 视图,移除了不必要的元素、让部分元素的格式更加清晰并增加了额外的功能。

       现在运行程序,Details 视图的效果应该像是这个样子:

image

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

强化模型

       现在,我们要来处理 Edit 视图。之前所说的绝大部分内容都可以应用到这个视图上,但仍然有些问题。我会演示其他的一些有用的 MVC 特性。下面是更新后的 Edit.aspx 视图:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<BasicMvcApplication.Models.Products>" %>
 
<asp:Content ID="Content1" COntentPlaceHolderID="TitleContent" runat="server">
    Edit
</asp:Content>
 
<asp:Content ID="Content2" COntentPlaceHolderID="MainContent" runat="server">
 
    <h2>Edit</h2>
 
    <% using (Html.BeginForm()) {%>        
        <fieldset>
            <legend>Edit Product Details</legend>
            <table>
                <tr><td>Product Name:</td><td><%: Html.TextBoxFor(e => e.ProductName) %></td></tr>
                <tr><td>Supplier:</td><td><%: Html.TextBoxFor(e => e.SupplierID) %></td></tr>
                <tr><td>Category:</td><td><%: Html.TextBoxFor(e => e.CategoryID) %></td></tr>
                <tr><td>Quantity per Unit:</td><td><%: Html.TextBoxFor(e => e.QuantityPerUnit) %></td></tr>
                <tr>
                    <td>Unit Price:</td>
                    <td><%: Html.TextBoxFor(e => e.UnitPrice, 
                            new { Value = string.Format("{0:F2}", Model.UnitPrice) })%></td>
                </tr>
                <tr><td>Units in Stock:</td><td><%: Html.TextBoxFor(e => e.UnitsInStock) %></td></tr>
                <tr><td>Units on Order:</td><td><%: Html.TextBoxFor(e => e.UnitsOnOrder) %></td></tr>
                <tr><td>Reorder Level:</td><td><%: Html.TextBoxFor(e => e.ReorderLevel) %></td></tr>
                <tr><td>Discontinued:</td><td><%: Html.TextBoxFor(e => e.Discontinued) %></td></tr>
            </table>
        </fieldset>
        <p>
            <input type="submit" value="Save" />
        </p>
    <% } %>
 
    <div>
        <%: Html.ActionLink("Back to List", "Index") %>
    </div>
 
</asp:Content>

       HTML 辅助方法 BeginForm 生成回发到控制器 URL 所需的表单 HTML,此处是 /Product/Edit/<产品 ID>。绝大部分数据字段使用了 HTML 辅助方法 TextBoxFor,它创建一个包含了我们指定的数据模型字段值的文本输入框。这里通过重写输入框的 value 属性来提供格式化后的 UnitPrice 值。但要注意,使用 string.Format 设置文本框的值时,要确保指定的 HTML 属性是 Value(首字母大写),否则 MVC 辅助方法将忽略格式化字符串

       现在运行程序,效果如下:

image

       Supplier 和 Category 还有问题,但先前的解决方案已经不起作用。因为需要从列表进行选择,而不是仅仅看到单一的值。下面我们将通过扩展数据模型并使用视图数据以及 HTML 辅助方法来解决这一问题。

       首先要扩展 NorthwindAccessConsolidator 类以获取供应商及类别名称的完整列表,然后从名称字符串取得 SupplierID 和 CategoryID:

public IEnumerable<string> GetAllSuppliers()
{
    return db.Suppliers.Select(e => e.CompanyName);
}
 
public int GetSupplierID(string name)
{
    return db.Suppliers.Where(e => e.CompanyName == name)
        .Select(e => e.SupplierID).Single();
}
 
public IEnumerable<string> GetAllCategories()
{
    return db.Categories.Select(e => e.CategoryName);
}
 
public int GetCategoryID(string name)
{
    return db.Categories.Where(e => e.CategoryName == name)
        .Select(e => e.CategoryID).Single();
}

 

       接着我们在 Models 目录中添加一个名为 ProductListWrapper 的类来扩展数据模型。它是一个简单的封装类:

namespace ExtendedModel.Models
{
    public class ProductListWrapper
    {
        public Products product { get; set; }
        public string SelectedSupplier { get; set; }
        public string SelectedCategory { get; set; }
    }
}

 

       修改控制器中第一个 Edit 方法以使它通过 View 方法返回 ProductListWrapper 的实例。用户在 Index 视图中单击 Edit 链接时会调用此方法。我们在方法中通过视图数据将 SelectList 类的实例传给视图。它是一个特殊的 MVC 类型,可以用来传送要呈现的对象列表

public ActionResult Edit(int id)
{
    ViewData["categories"] = new SelectList(nwa.GetAllCategories());
    ViewData["suppliers"] = new SelectList(nwa.GetAllSuppliers());
 
    Products prod = nwa.GetProduct(id);
    ProductListWrapper wrap = new ProductListWrapper()
    {
        product = prod,
        SelectedCategory = prod.Categories.CategoryName,
        SelectedSupplier = prod.Suppliers.CompanyName
    };
 
    return View(wrap);
}

 

       接着,要更新 Edit.aspx,这样视图才知道如何呈现新数据类型。首先修改页面定义使之指向封装类型:

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage<ExtendedModel.Models.ProductListWrapper>" %>

       视图中的模型引用要修改为 e.product.Fields 格式以反映封装类型的结构:

<tr><td>Product Name:</td><td><%: Html.TextBoxFor(e => e.product.ProductName) %></td></tr>
......

       通过 HTML 辅助方法 DropDownListFor 来达到使用视图数据中新增的 SelectList 实例:

<tr>
    <td>Supplier:</td>
    <td><%: Html.DropDownListFor(e => e.SelectedSupplier, 
            ViewData["suppliers"] as SelectList)%></td>
</tr>
<tr>
    <td>Category:</td>
    <td><%: Html.DropDownListFor(e => e.SelectedCategory,
            ViewData["categories"] as SelectList)%></td>
</tr>

 

       最后一步是修改回发修改时调用的控制器方法 Edit。对于这个方法,我们需要解压被封装的 Product 实例,更新 SupplierID 和 CategoryID 的值让它们和用户的选择相匹配,然后保存:

[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
    try
    {
        Products prod = nwa.GetProduct(id);
        if (prod != null)
        {
            ProductListWrapper wrapper = new ProductListWrapper()
            {
                product = prod
            };
            UpdateModel(wrapper);
            prod.SupplierID = nwa.GetSupplierID(wrapper.SelectedSupplier);
            prod.CategoryID = nwa.GetCategoryID(wrapper.SelectedCategory);
            nwa.SaveChanges();
            return RedirectToAction("Index");
        }
        else
        {
            throw new NoSuchRecordException();
        }
    }
    catch
    {
        return View();
    }
}

       现在运行程序,呈现的效果如下:

image

       通过向数据模型增加一些简单的代码,同时更新控制器和视图,就可以把数值型的外键映射为用户能够理解并可从列表选择的内容。通过很少的调整就可以创造很大的价值,这展示了 MVC 框架的灵活性。


推荐阅读
  • 本文介绍了Sencha Touch的学习使用心得,主要包括搭建项目框架的过程。作者强调了使用MVC模式的重要性,并提供了一个干净的引用示例。文章还介绍了Index.html页面的作用,以及如何通过链接样式表来改变全局风格。 ... [详细]
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了django中视图函数的使用方法,包括如何接收Web请求并返回Web响应,以及如何处理GET请求和POST请求。同时还介绍了urls.py和views.py文件的配置方式。 ... [详细]
  • 导出功能protectedvoidbtnExport(objectsender,EventArgse){用来打开下载窗口stringfileName中 ... [详细]
  • intellij idea的安装与使用(保姆级教程)
    intellijidea的安装与使用(保姆级教程)IntelliJ在业界被公认为最好的java开发工具,尤其在智能代码助手、代码自动提示、重构、JavaEE支持、各类版本工具(gi ... [详细]
  • Day2列表、字典、集合操作详解
    本文详细介绍了列表、字典、集合的操作方法,包括定义列表、访问列表元素、字符串操作、字典操作、集合操作、文件操作、字符编码与转码等内容。内容详实,适合初学者参考。 ... [详细]
  • 从零基础到精通的前台学习路线
    随着互联网的发展,前台开发工程师成为市场上非常抢手的人才。本文介绍了从零基础到精通前台开发的学习路线,包括学习HTML、CSS、JavaScript等基础知识和常用工具的使用。通过循序渐进的学习,可以掌握前台开发的基本技能,并有能力找到一份月薪8000以上的工作。 ... [详细]
  • Asp.net Mvc Framework 七 (Filter及其执行顺序) 的应用示例
    本文介绍了在Asp.net Mvc中应用Filter功能进行登录判断、用户权限控制、输出缓存、防盗链、防蜘蛛、本地化设置等操作的示例,并解释了Filter的执行顺序。通过示例代码,详细说明了如何使用Filter来实现这些功能。 ... [详细]
author-avatar
手机用户2502908277
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有