MVC3

摘要:

  • 视图的作用
  • 如何指定视图
  • 强类型视图
  • 理解视图模型
  • 如何添加视图
  • Razor的使用方法
  • 如何指定分部视图
  • 理解视图引擎

1.视图的作用

在ASP.NET MVC中向用户提供用户界面的过程由两部分组成

  • 检查由控制器提交的ViewDataDictionary(通过ViewData属性访问)
  • 将其内容转换为HTML格式

并非所有的视图都将渲染HTML格式,视图也可以渲染其它类型的内容。


从ASP.NET MVC3开始,视图数据可以通过ViewBag属性访问。ViewBag属性是动态的,可以通过ViewData属性访问的相同数据。

ViewBag.Message等同于ViewData["Message"],但彼此之间并不存在真正的技术优势。

但二者差异:

  • 当要访问的关键字是一个有效的C#标识符时,ViewBag才起作用。如,在ViewData["Key With Spaces"]中存放一个值,那么将不能使用ViewBag访问这个值
  • 这个动态的值不能作为一个参数传递给扩展的方法。因为编译器为了选择正确的扩展方法,在编译时必须知道每一个参数的真正类型。如,@Html.TextBox("name",ViewBag.Name)将编译失败,需显式转换

<body>
    <h1>@ViewBag.Message</h1>
    <p>...</p>
</body>

视图并不像ASP.NET Web Forms和PHP,它本身不会被直接访问,浏览器不能指向一个视图并渲染它。

然而,视图总是被一个控制器渲染,该控制器向它提供了要渲染的数据。

2.指定视图

  • 每个控制器在Views目录下都有一个对应的文件夹,其名称与控制器一样。
  • 每一个控制器的View文件夹中,每一个操作方法都有一个名称相同的视图文件与之相对应。操作方法通过View方法返回ViewResult对象。如,
  •  return View();
  • 当控制器操作没有指定视图的名称时,操作方法返回的ViewResult将按照约定来确定视图(先在目录/Views/ControllerName下查找与控制器名称(不带Controller)相同的视图)。与APS.NET MVC的大部分约定设置一样,这一约定是可以重写的,指定不同视图(可以指定完全位于不同目录结构的视图)如,
  • return View("NotIndex");
    return View("~/View/Example/Index.cshtml");//避开视图引擎的内部查找机制,需使用文件扩展名

3.强类型视图

创建Album实例列表并在视图中迭代它们:

public  ActionResult List()
{
    var albums = new List<Albums>();
    for(int i = 0;i<10;i++)
    {
        albums.Add(new Albums{Title = "Product" + i });
    }
    ViewBag.Albums = albums;
    return View();
}

//视图
<ul>
@foreach(Album a in (ViewBag.Albums as IEnumerable<Album>))
{
    <li>@a.Title</li>
}
</ul>
View Code

在枚举之前需将动态的ViewBag.Albums转换为IEnumerable<Album>类型,也可以使用dynamic使代码更整洁,但这样不能使用代码智能感知

<ul>
@foreach(dynamic p in ViewBag.Albums)
{
    <li>@p.Title</li>
}
</ul>

在Controller方法中,可以通过向重载的View方法中传递模型实例来指定模型,在后台传进View方法的值将赋值给ViewData.Model属性,如

return View(albums);

接下来,告知视图哪种类型的模式正在使用@model声明,注意需输入完全限定类型名,如

@model IEnumerable<MvcApplication1.Models.Album>
<ul>
@foreach(Album p in Model){
    <li>@p.Title</li>
</ul>

如果不像输入模型类型的完全限定类型名,可以使用@using关键字声明,如

@using MvcApplication1.Models
@model IEnumerable<Album>

对于在视图中经常使用的名称空间,比较好的方法是在Views目录下的web.config文件中声明,如

@using MvcApplication1.Models
<system.web.webPages.razor>
    ...
    <pages pageBaseType="System.Web.Mvc.WebViewPage">
        <namespaces>
            <add namespace="System.Web.Mvc" />
            ...
            <add namespace="MvcApplication1.Models />
            ...
        </namespaces>
    <pages>
</system.web.webPages.razor>

4.视图模型

视图通常需要显示没有直接映射到域模型的数据,当需要显示与视图主模型无关的额外数据时:

  • 把数据存放ViewBag属性(优点:方便使用  缺点:如果严格控制流进视图的数据,就必须使所有类型都是强数据的,以便利用智能感知)
  • 编写自定义的视图模型类

 如创建购物车汇总页面:

public class ShoppingCartViewModel{
    public IEnumerable<Product> Products{get;set;}
    public decimal CartTotal{get;set;}
    public string Message{get;set;}
}

//输入视图
@model ShoppingCartSummaryViewModel

5.添加视图及自定义T4视图模板(后者需上机测试,不理解

6.Razor视图引擎

Razor提供了一个干净、轻量级、简单的视图引擎,不包含原有的Web Forms视图引擎中包含的“语法累赘”。最大限度地减少了语法和额外的字符。

代码表达式

Razor中的核心转换字符是@,用作标记-代码的转换字符,有时也反过来用作代码-标记的转换字符。这里一共有两种基本类型的转换:代码表达式和代码块

WebForm视图只支持显式代码表达式,而ASP.NET MVC支持隐式代码表达式求解。

<h1>Listing @stuff.Length items.</h1>//ASP.NET MVC
<h1>Listing <%:@stuff.Length %>items.</h1>//WEB Form

 Razor十分智能,可以知道表达式后面的空格不是一个有效的标识符,所以可以顺畅地转回到标记语言。

下面的情况也可以准确的推断,自动从代码转回标记:

<ul>
@foreach(var item in items){
    <li>The item name is @item.</li>//Razor可以知道@item后的字符不是引用属性或方法,因为它看到在.后是尖括号
}
</ul>

 但下面的情况会出现潜在的二义性,想要的输出结果是<span>MyApp.Models</span>,但系统会提示string没有Models属性:

@{
    string rootNamespace = "MyApp";
}
<span>@rootNamespace.Models</span>

解决这种问题可以将表达式用圆括号括起来以支持显式代码表达式:

<span>@(rootNamespace).Models</span>

Razor足够智能,可以辨别出电子地址的一般模式,而不会处理这种形式的表达式。(内部采用了一个简单的算法,即采用@@符号转义一个@符号)

但当遇到向将这种形式字符串作为表达式时,需用圆括号指明,如

<li>Item_@item.Length</li>//这种情况下会匹配成邮件地址
<li>Item_@(item.Length)</li>//解决

当需要显示一些以@符号开头的语句时,Razor会先尝试解析这些隐式代码表达式,失败后使用@@来转义@符号

Html编码:

Razor表达式时用Html编码的

如果想展示Html标记,就返回一个System.Web.IHtmlString对象的实例,Razor并不对它进行编码。也可以创建一个HtmlString实例或者使用Html.Raw便捷方法。如

//将不会弹出警告,而会显示出编码信息
@{    string message = "<script>alert('haacked!');</script>";
}
<span>@message</span>

//将显示不经过Html编码的消息
@{    string message = "<strong>This is bold!</strong>";
}
<span>Html.Raw(@message)</span>

但在JS中,仅仅是HTML编码是不够的,还需要使用@Ajax.JavaScriptStringEncode方法对用户输入进行编码,避免XSS攻击。如

<script>
    $(function(){
        var message = 'Hello @ViewBag.Username';
        ...
</script>

//需修改为
<script>
    $(function(){
        var message = 'Hello @Ajax.JavaScriptStringEncode(@ViewBag.Username)');
        ...
</script>

代码块

Razor理解HTML标记语言的结构,可以自动转换代码。但相比之下,WebForm视图引擎就需要显式支出代码和标记之间转换的代码。如

@foreach(var item in stuff){
    <li>The item name is @item.</li>
}

//Web Form视图引擎
<% foreach(var item in stuff){ %>
    <li>The item name is <%: item %>.</li>
<% } %>

代码块除了需要@符号分割之外还需要使用花括号,如

@{
    string是= “...”;
    ViewBag.Title " Another line of code";
}
//
@{Html.RenderPartial("SomePartial");}

注意:代码块中的语句(如foreach循环和if代码块中的语句)是不需要使用花括号的。

Razor语法示例

a.隐式代码表达式:Razor中的隐式代码表达式总是采用HTML编码方式。

//Razor
<span>@model.Message</span>
//Web Forms
<span><%:model.Message %></span>

b.显示代码表达式:

//Razor
<span>ISBN@(isbn)</span>
//Web Forms
<span>ISBN<%: isbn %></span

c.无编码代码表达式:

//Razor
<span>@Html.Raw(model.Message)</span>
//Web Forms
<span><%:Html.Raw(model.Message) %></span>

d.代码块:

不像代码表达式先求得表达式的值,然后再输出到响应,代码块是简单的执行代码的部分,对于声明以后要使用的变量是有用的

//Razor
@{
    int x = 123;
    string y = "because";
}
//Web Forms
<%
    int x = 123;
    string y = "because";
%>

e.文本和标记相结合:

//Razor
@foreach(var item in items){
    <span>The item name is @item.Name.</span>
}
//Web Form
<% foreach(var item in items){ %>
    <span>Item <%: item.Name %>.</span>
<% } %>

f.混合代码和纯文本:Razor查找标签的开始位置以确定何时将代码转换为标记

//Razor
@if(showMessage){
    <text>This is plain text</text>
}
或
@if(showMessage){
    @:This is plain text.
}
//Web Form
<% if(showMessage) {%>
    This is plain text.
<% } %>

g.转义代码分隔符:可以用“@@”来编码“@”达到显示的目的。

//Razor
My Twitter Handle is &#64;hacked
或
My Twitter Handle is @@hacked
//Web Form
&lt;% express %&gt;marks a code nugget.

h.服务器端的注释:

//Razor
@*
This is a multiline server side comment.
@if(showMessage){
    <h1>@ViewBag.Message</h1>
}
ALL of this is commented out.
*@
//Web Form
<%--
This is a multiline server side comment.
<% if(showMessage) {%>
    <h1><%: ViewBag.Message %></h1>
<% } %>
All of this is commented out.
--%>

i.调用泛型方法:需整个表达式用圆括号括起来,因为调用泛型方法的代码包含尖括号,尖括号会导致Razor转回标记

//Razor
@(Html.SomeMethod<AType>())
//Web Form
<%: Html.SomeMethod<AType>() %>

布局

Razor的布局有助于应用程序中的多个视图保持一致的外观。

可以使用布局为网站定义公共模板,公共模板包含一个或多个占位符,应用程序中的其他视图为它们提供内容。

<!DOCTYPE html>
//视图
<html>
<head><title>@ViewBag.Title</title></head>
<body>
    <h1>@ViewBag.Title</h1>
    <div id="main-content">@RenderBody()</div>
    <footer>@RenderSection("Footer")</footer>
</body>
</html>


//定义
@{
    Layout = "~</Views/Shared/SiteLayout.cshtml";
    View.Title = "The Index!";
}
<p>This is the main content!</p>    

@section Footer{
    This is the <strong>footer</strong>
}
View Code

 渲染出的内容:

<!DOCTYPE html>
<html>
<head><title>The Index!</title></head>
<body>
    <h1>The Index!</h1>
    <div id="main-content"><p>This is  the main content!></p></div>
    <footer>This is the <strong> footer</strong></footer>
</body>
</html>

 RenderSection方法有一个重载版本,运行指定不需要的节。

<footer>@RenderSection("Footer",false)</footer>

也可以通过以下方式来定义默认值

<footer>
    @if(IsSectionDefined("Footer")) {
        RenderSection("Footer");
    }
    else{
        <span>This is the default footer.</span>
    }
</footer>

ViewStart

消除冗余的视图布局指定代码。

_ViewStart.cshtml的文件代码先于任何视图运行,所以一个视图可以重写Layout属性的默认值,从而重新选择一个不同的布局。

但如果一组视图拥有共同的设置,那么可以在这个文件中统一设置。

7.指定分部视图

操作方法可以通过PartialView方法以PartialViewResult的形式返回分部视图。如,

return PartialView();

但如果布局是由_ViewStart.cshtml页面指定(而不是直接在视图中)的,那么布局将无法渲染。

分部分视图在使用AJAX技术进行部分更新的情形中是有用的。

8.视图引擎

ASP.NET MVC的生命周期

HTTP请求—路由—控制器—视图结果—视图引擎—视图—响应

9.配置视图引擎

视图引擎配置在文件Global.asax.cs

在Application_Start方法中编写代码替代默认视图引擎

protected void Application_Start(){
    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new MyViewEngine());
    RegisterRoutes(RouteTable.Routes);
}

创建自定义视图引擎时,需要实现IViewEngine接口和IView接口(此部分内容较多,参考59~63页

原文地址:https://www.cnblogs.com/KevinG/p/3560658.html